# HG changeset patch # User jld # Date 1264505267 0 # Node ID d6c0c0e0431caf8093e115cc46d998974f7ab071 # Parent 0cdf7781a5abb0399dff23a47edccc9353c1409e * net/lwip_tcpip/current/*: Major update of lwIP TCP/IP stack to version 1.3.2. * io/eth/current/cdl/eth_drivers.cdl, include/eth_drv.h: Minor changes for lwIP 1.3.x. * io/eth/current/src/lwip/eth_drv.c: Rework for lwIP 1.3.x based on standalone driver for RedBoot. * io/eth/current/src/lwip/eth_conf.inl: Add static interface configuration. diff --git a/packages/io/eth/current/ChangeLog b/packages/io/eth/current/ChangeLog --- a/packages/io/eth/current/ChangeLog +++ b/packages/io/eth/current/ChangeLog @@ -1,3 +1,11 @@ +2010-01-25 Simon Kallweit + + * cdl/eth_drivers.cdl, include/eth_drv.h: Minor changes for + lwIP 1.3.x. + * src/lwip/eth_drv.c: Rework for lwIP 1.3.x based on standalone + driver for RedBoot. + * src/lwip/eth_conf.inl: Add static interface configuration. + 2009-07-24 Edgar Grimberg * cdl/eth_drivers.cdl: Added the suppress option for diff --git a/packages/io/eth/current/cdl/eth_drivers.cdl b/packages/io/eth/current/cdl/eth_drivers.cdl --- a/packages/io/eth/current/cdl/eth_drivers.cdl +++ b/packages/io/eth/current/cdl/eth_drivers.cdl @@ -173,7 +173,7 @@ cdl_package CYGPKG_IO_ETH_DRIVERS { cdl_component CYGPKG_IO_ETH_DRIVERS_STAND_ALONE { display "Support for stand-alone network stack." flavor bool - active_if !CYGPKG_NET + active_if { !CYGPKG_NET && !CYGPKG_NET_LWIP } requires CYGINT_ISO_STRING_MEMFUNCS default_value 1 compile stand_alone/eth_drv.c diff --git a/packages/io/eth/current/include/eth_drv.h b/packages/io/eth/current/include/eth_drv.h --- a/packages/io/eth/current/include/eth_drv.h +++ b/packages/io/eth/current/include/eth_drv.h @@ -8,7 +8,7 @@ // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2009 Free Software Foundation, Inc. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free @@ -154,6 +154,9 @@ struct arpcom { #ifdef CYGPKG_NET_LWIP struct arpcom { struct netif ac_if; +# ifdef CYGFUN_LWIP_MODE_SEQUENTIAL + cyg_sem_t send_sem; +# endif }; #endif diff --git a/packages/io/eth/current/src/lwip/eth_conf.inl b/packages/io/eth/current/src/lwip/eth_conf.inl new file mode 100644 --- /dev/null +++ b/packages/io/eth/current/src/lwip/eth_conf.inl @@ -0,0 +1,157 @@ +//========================================================================== +// +// src/lwip/eth_conf.inl +// +// Static interface configuration +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2009-06-09 +// Purpose: Static interface configuration +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include + +// Ethernet device configuration +struct eth_conf { + cyg_uint8 def; // This is the default netif + cyg_uint8 dhcp; // Use DHCP client to get address + cyg_uint8 addr[4]; // IPv4 address + cyg_uint8 netmask[4]; // IPv4 netmask + cyg_uint8 gateway[4]; // IPv4 gateway +}; + +// Ethernet device configuration table +static struct eth_conf eth_conf_table[] = { +// Ethernet device 0 +#ifdef CYGPKG_LWIP_ETH0_CONF + { +# ifdef CYGDAT_LWIP_ETH0_DEFAULT + .def = 1, +# endif +# ifdef CYGDAT_LWIP_ETH0_DHCP + .dhcp = 1, +# endif +# ifdef CYGDAT_LWIP_ETH0_NETCONF_ADDR + .addr = { CYGDAT_LWIP_ETH0_NETCONF_ADDR }, +# endif +# ifdef CYGDAT_LWIP_ETH0_NETCONF_NETMASK + .netmask = { CYGDAT_LWIP_ETH0_NETCONF_NETMASK }, +# endif +# ifdef CYGDAT_LWIP_ETH0_NETCONF_GATEWAY + .gateway = { CYGDAT_LWIP_ETH0_NETCONF_GATEWAY }, +# endif + }, +#endif // CYGPKG_LWIP_ETH0_CONF +// Ethernet device 1 +#ifdef CYGPKG_LWIP_ETH1_CONF + { +# ifdef CYGDAT_LWIP_ETH1_DEFAULT + .def = 1, +# endif +# ifdef CYGDAT_LWIP_ETH1_DHCP + .dhcp = 1, +# endif +# ifdef CYGDAT_LWIP_ETH1_NETCONF_ADDR + .addr = { CYGDAT_LWIP_ETH1_NETCONF_ADDR }, +# endif +# ifdef CYGDAT_LWIP_ETH1_NETCONF_NETMASK + .netmask = { CYGDAT_LWIP_ETH1_NETCONF_NETMASK }, +# endif +# ifdef CYGDAT_LWIP_ETH1_NETCONF_GATEWAY + .gateway = { CYGDAT_LWIP_ETH1_NETCONF_GATEWAY }, +# endif + }, +#endif // CYGPKG_LWIP_ETH1_CONF +// Ethernet device 2 +#ifdef CYGPKG_LWIP_ETH2_CONF + { +# ifdef CYGDAT_LWIP_ETH2_DEFAULT + .def = 1, +# endif +# ifdef CYGDAT_LWIP_ETH2_DHCP + .dhcp = 1, +# endif +# ifdef CYGDAT_LWIP_ETH2_NETCONF_ADDR + .addr = { CYGDAT_LWIP_ETH2_NETCONF_ADDR }, +# endif +# ifdef CYGDAT_LWIP_ETH2_NETCONF_NETMASK + .netmask = { CYGDAT_LWIP_ETH2_NETCONF_NETMASK }, +# endif +# ifdef CYGDAT_LWIP_ETH2_NETCONF_GATEWAY + .gateway = { CYGDAT_LWIP_ETH2_NETCONF_GATEWAY }, +# endif + }, +#endif // CYGPKG_LWIP_ETH2_CONF +// Ethernet device 3 +#ifdef CYGPKG_LWIP_ETH3_CONF + { +# ifdef CYGDAT_LWIP_ETH3_DEFAULT + .def = 1, +# endif +# ifdef CYGDAT_LWIP_ETH3_DHCP + .dhcp = 1, +# endif +# ifdef CYGDAT_LWIP_ETH3_NETCONF_ADDR + .addr = { CYGDAT_LWIP_ETH3_NETCONF_ADDR }, +# endif +# ifdef CYGDAT_LWIP_ETH3_NETCONF_NETMASK + .netmask = { CYGDAT_LWIP_ETH3_NETCONF_NETMASK }, +# endif +# ifdef CYGDAT_LWIP_ETH3_NETCONF_GATEWAY + .gateway = { CYGDAT_LWIP_ETH3_NETCONF_GATEWAY }, +# endif + }, +#endif // CYGPKG_LWIP_ETH3_CONF +}; + +#define NUM_ETH_CONF (sizeof(eth_conf_table) / sizeof(struct eth_conf)) + +static int eth_conf_index; + +static struct eth_conf *next_eth_conf(void) +{ + if (eth_conf_index < NUM_ETH_CONF) + return ð_conf_table[eth_conf_index++]; + + return NULL; +} diff --git a/packages/io/eth/current/src/lwip/eth_drv.c b/packages/io/eth/current/src/lwip/eth_drv.c --- a/packages/io/eth/current/src/lwip/eth_drv.c +++ b/packages/io/eth/current/src/lwip/eth_drv.c @@ -2,13 +2,13 @@ // // src/lwip/eth_drv.c // -// Hardware independent ethernet driver for lwIP +// Hardware independent networking support for lwIP // //========================================================================== // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +// Copyright (C) 2008, 2009 Free Software Foundation, Inc. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free @@ -39,12 +39,12 @@ //========================================================================== //#####DESCRIPTIONBEGIN#### // -// Author(s): Jani Monoses -// Contributors: -// Date: 2002-04-05 -// Purpose: Hardware independent ethernet driver +// Author(s): Simon Kallweit +// Contributors: +// Date: 2008-12-09 +// Purpose: Hardware independent networking support for lwIP. // Description: Based on the standalone driver for RedBoot. -// +// //####DESCRIPTIONEND#### // //========================================================================== @@ -64,77 +64,52 @@ #include #include -#include "lwip/opt.h" -#include "lwip/ip.h" -#include "lwip/mem.h" -#include "lwip/pbuf.h" -#include "lwip/sys.h" - -#include "netif/etharp.h" - - -// Interfaces exported to drivers - -static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr); -static void eth_drv_recv(struct eth_drv_sc *sc, int total_len); -static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status); - -struct eth_drv_funs eth_drv_funs = { eth_drv_init, eth_drv_recv, eth_drv_tx_done }; +#include +#include +#include +#include +#include +#include +#include -#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG -int cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY; -#endif - -extern void lwip_dsr_stuff(void); -extern void lwip_set_addr(struct netif *); -extern void lwip_dhcp_init(struct netif *); +#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT -//DSR called from the low level driver.Signals the input_thread -void -eth_drv_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) -{ - struct eth_drv_sc *sc = (struct eth_drv_sc *) data; - sc->state |= ETH_DRV_NEEDS_DELIVERY; - lwip_dsr_stuff(); -} +#include -err_t ecosif_init(struct netif *netif); +// Use with care! Local variable defined! +#define START_CONSOLE() \ +{ /* NEW BLOCK */ \ + int _cur_console = \ + CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); \ + { \ + int i; \ + if ( CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET, \ + "info_console_force", &i, \ + CYGNUM_FLASH_CFG_TYPE_CONFIG_BOOL ) ) { \ + if ( i ) { \ + if ( CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET, \ + "info_console_number", &i, \ + CYGNUM_FLASH_CFG_TYPE_CONFIG_INT ) ){ \ + /* Then i is the console to force it to: */ \ + CYGACC_CALL_IF_SET_CONSOLE_COMM( i ); \ + } \ + } \ + } \ + } -// This function is called during system initialization to register a -// network interface with the system. -static void -eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr) -{ - struct netif *netif = &sc->sc_arpcom.ac_if; - - netif->state = sc; - ecosif_init(netif); - - // enaddr == 0 -> hardware init was incomplete (no ESA) - if (enaddr != 0) { - // Set up hardware address - memcpy(netif->hwaddr, enaddr, ETHER_ADDR_LEN); - // Perform any hardware initialization - (sc->funs->start) (sc, (unsigned char *) &netif->hwaddr, 0); - } -#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_DIAG -// Set up interfaces so debug environment can share this device - { - void *dbg = CYGACC_CALL_IF_DBG_DATA(); - if (!dbg) { - CYGACC_CALL_IF_DBG_DATA_SET((void *)sc); - } - } +#define END_CONSOLE() \ + CYGACC_CALL_IF_SET_CONSOLE_COMM(_cur_console); \ +} /* END BLOCK */ + +#else +#define START_CONSOLE() +#define END_CONSOLE() #endif - // - // we call this after the driver was started successfully - // - lwip_dhcp_init(netif); -} +// ------------------------------------------------------------------------ // // Control whether any special locking needs to take place if we intend to -// cooperate with a ROM monitor (e.g. RedBoot) using this hardware. +// cooperate with a ROM monitor (e.g. RedBoot) using this hardware. // #if defined(CYGSEM_HAL_USE_ROM_MONITOR) && \ defined(CYGSEM_HAL_VIRTUAL_VECTOR_DIAG) && \ @@ -147,49 +122,142 @@ eth_drv_init(struct eth_drv_sc *sc, unsi // using the network hardware for the debug channel. #define RedBoot_TCP_CHANNEL CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS +// Define this if you ever need to call 'diag_printf()' from interrupt level +// code (ISR) and the debug channel might be using the network hardware. If +// this is not the case, then disabling interrupts here is over-kill. +//#define _LOCK_USING_INTERRUPTS #endif -// -// Send a packet of data to the hardware -// +// Static device configurations + +#include "eth_conf.inl" + +// Interfaces exported to drivers + +static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr); +static void eth_drv_recv(struct eth_drv_sc *sc, int total_len); +static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status); -static void -eth_drv_send(struct netif *netif, struct pbuf *p) -{ - struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; - struct eth_drv_sc *sc = netif->state; - int sg_len = 0; - struct pbuf *q; +struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done}; + +static void eth_drv_send(struct netif *netif, struct pbuf *p); -#ifdef _LOCK_WITH_ROM_MONITOR - bool need_lock = false; - int debug_chan; +#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG +int cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY; +// Usually just the header is enough, the body slows things too much. +#define DIAG_DUMP_BUF_HDR( a, b ) if (0 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) ) +#define DIAG_DUMP_BUF_BDY( a, b ) if (1 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) ) +#else +#define DIAG_DUMP_BUF_HDR( a, b ) +#define DIAG_DUMP_BUF_BDY( a, b ) #endif - while (!(sc->funs->can_send) (sc)); +#define MAX_ETH_MSG 1540 + +// Interface to lwIP glue code + +extern void lwip_eth_drv_new(struct netif *netif); +extern void lwip_eth_drv_dsr(void); +extern err_t lwip_eth_drv_input(struct pbuf *p, struct netif *netif); + + + +// +// Called by lwIP to do the actual transmission of a packet. The packet passed +// to this function may be chained. This function passes the data to the +// hardware driver +// +static err_t eth_netif_linkoutput(struct netif *netif, struct pbuf *p) +{ + eth_drv_send(netif, p); + + return ERR_OK; +} + +// +// Called by lwIP when an IP packet should be sent. +// +static err_t eth_netif_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr) +{ + // Resolve hardware address, then send (or queue) packet + return etharp_output(netif, p, ipaddr); +} + +// +// Called by lwIP to init the netif. +// +static err_t eth_netif_init(struct netif *netif) +{ + netif->name[0] = 'e'; + netif->name[1] = 't'; + netif->hwaddr_len = 6; + netif->output = eth_netif_output; + netif->linkoutput = eth_netif_linkoutput; + netif->mtu = 1500; + + netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + return ERR_OK; +} - for (q = p; q != NULL; q = q->next) { - sg_list[sg_len].buf = (CYG_ADDRESS) q->payload; - sg_list[sg_len++].len = q->len; - } -#ifdef _LOCK_WITH_ROM_MONITOR - debug_chan = CYGACC_CALL_IF_SET_DEBUG_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); - if (debug_chan == RedBoot_TCP_CHANNEL) { - need_lock = true; - cyg_drv_dsr_lock(); - } -#endif // _LOCK_WITH_ROM_MONITOR +// +// This function is called during system initialization to register a +// network interface with the system. +// +static void +eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr) +{ + struct netif *netif = &sc->sc_arpcom.ac_if; + struct eth_conf *conf; + struct ip_addr addr, netmask, gateway; + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL + cyg_semaphore_init(&sc->sc_arpcom.send_sem, 1); +#endif + + // enaddr == 0 -> hardware init was incomplete (no ESA) + if (enaddr == 0) + return; + + netif->state = sc; - (sc->funs->send) (sc, sg_list, sg_len, p->tot_len, - (CYG_ADDRWORD) p); - -#ifdef _LOCK_WITH_ROM_MONITOR - // Unlock the driver & hardware. It can once again be safely shared. - if (need_lock) { - cyg_drv_dsr_unlock(); - } -#endif // _LOCK_WITH_ROM_MONITOR - + // Configure the IP address + conf = next_eth_conf(); + if (conf && !conf->dhcp) { + IP4_ADDR(&addr, conf->addr[0], conf->addr[1], + conf->addr[2], conf->addr[3]); + IP4_ADDR(&netmask, conf->netmask[0], conf->netmask[1], + conf->netmask[2], conf->netmask[3]); + IP4_ADDR(&gateway, conf->gateway[0], conf->gateway[1], + conf->gateway[2], conf->gateway[3]); + } else { + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gateway, 0, 0, 0, 0); + } + + netif_add(netif, &addr, &netmask, &gateway, netif->state, + eth_netif_init, lwip_eth_drv_input); + + lwip_eth_drv_new(netif); + + if (conf && conf->def) + netif_set_default(netif); + + // Set up hardware address + memcpy(netif->hwaddr, enaddr, ETHER_ADDR_LEN); + (sc->funs->start)(sc, (unsigned char *) &netif->hwaddr, 0); + + if (conf && !conf->dhcp) + netif_set_up(netif); + +#if LWIP_DHCP + // Start DHCP if configured + if (conf && conf->dhcp) { + dhcp_start(netif); + } +#endif } // @@ -199,18 +267,127 @@ eth_drv_send(struct netif *netif, struct static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status) { -#if 0 - struct pbuf *p = (struct pbuf *)key; - struct netif *netif = &sc->sc_arpcom.ac_if; - - CYGARC_HAL_SAVE_GP(); - CYGARC_HAL_RESTORE_GP(); -#endif +#ifdef CYGFUN_LWIP_MODE_SIMPLE + // Stop polling + cyg_bool *done = (cyg_bool *) key; + *done = true; +#endif +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL + // Wake-up the lwip-thread + cyg_semaphore_post(&sc->sc_arpcom.send_sem); +#endif } -static void ecosif_input(struct netif *netif, struct pbuf* pbuf); +// +// Send a packet of data to the hardware. +// +static void +eth_drv_send(struct netif *netif, struct pbuf *p) +{ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct eth_drv_sc *sc = netif->state; + int sg_len = 0; + struct pbuf *q; +#ifdef _LOCK_WITH_ROM_MONITOR +#ifdef _LOCK_USING_INTERRUPTS + cyg_uint32 ints; +#endif + bool need_lock = false; + int debug_chan; +#endif +#ifdef CYGFUN_LWIP_MODE_SIMPLE + cyg_bool done = false; + int wait_cycles = 100; +#endif + +#ifdef CYGFUN_LWIP_MODE_SIMPLE + // Wait until we can send + while (!(sc->funs->can_send)(sc)) { + // Give driver a chance to service hardware + (sc->funs->poll)(sc); + if (--wait_cycles <= 0) { + START_CONSOLE(); + diag_printf("cannot send packet\n"); + END_CONSOLE(); + return; + } + } +#endif // CYGFUN_LWIP_MODE_SIMPLE + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL + // Wait until we can send + if ((sc->funs->can_send)(sc) < 1) + cyg_semaphore_wait(&sc->sc_arpcom.send_sem); + if ((sc->funs->can_send)(sc) < 1) + CYG_FAIL("cannot send packet"); +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL + + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (CYG_ADDRESS) q->payload; + sg_list[sg_len++].len = q->len; + } + +#ifdef _LOCK_WITH_ROM_MONITOR + // Firm lock on this portion of the driver. Since we are about to + // start messing with the actual hardware, it is imperative that the + // current thread not loose control of the CPU at this time. Otherwise, + // the hardware could be left in an unusable state. This caution is + // only warranted if there is a possibility of some other thread trying + // to use the hardware simultaneously. The network stack would prevent + // this implicitly since all accesses are controlled by the "splX()" + // locks, but if there is a ROM monitor, such as RedBoot, also using + // the hardware, all bets are off. -#define MAX_ETH_MSG 1540 + // Note: these operations can be avoided if it were well known that + // RedBoot was not using the network hardware for diagnostic I/O. This + // can be inferred by checking which I/O channel RedBoot is currently + // hooked to. + debug_chan = CYGACC_CALL_IF_SET_DEBUG_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); + if (debug_chan == RedBoot_TCP_CHANNEL) { + need_lock = true; +#ifdef _LOCK_USING_INTERRUPTS + HAL_DISABLE_INTERRUPTS(ints); +#endif + cyg_drv_dsr_lock(); + } +#endif // _LOCK_WITH_ROM_MONITOR + +#ifdef CYGFUN_LWIP_MODE_SIMPLE + // Tell hardware to send this packet + if (sg_len) + (sc->funs->send)(sc, sg_list, sg_len, p->tot_len, (CYG_ADDRWORD) &done); + // Wait until packet has been sent + wait_cycles = 100; + while (!done) { + (sc->funs->poll)(sc); + if (--wait_cycles <= 0) { + START_CONSOLE(); + diag_printf("packet failed to send\n"); + END_CONSOLE(); + break; + } + } +#endif // CYGFUN_LWIP_MODE_SIMPLE + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL + // Tell hardware to send this packet + if (sg_len) + (sc->funs->send)(sc, sg_list, sg_len, p->tot_len, 0); +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL + +#ifdef _LOCK_WITH_ROM_MONITOR + // Unlock the driver & hardware. It can once again be safely shared. + if (need_lock) { + cyg_drv_dsr_unlock(); +#ifdef _LOCK_USING_INTERRUPTS + HAL_RESTORE_INTERRUPTS(ints); +#endif + } +#endif // _LOCK_WITH_ROM_MONITOR +#undef _LOCK_WITH_ROM_MONITOR +} + // // This function is called from a hardware driver to indicate that an input // packet has arrived. The routine will set up appropriate network resources @@ -219,112 +396,51 @@ static void ecosif_input(struct netif *n static void eth_drv_recv(struct eth_drv_sc *sc, int total_len) { - struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; - struct netif *netif = &sc->sc_arpcom.ac_if; - - struct pbuf *p, *q; - - int sg_len = 0; - CYGARC_HAL_SAVE_GP(); + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct netif *netif = &sc->sc_arpcom.ac_if; + struct pbuf *p, *q; + int sg_len = 0; + + CYG_ASSERT(total_len != 0, "total_len is zero!"); + CYG_ASSERT(total_len >= 0, "total_len is negative!"); + + CYGARC_HAL_SAVE_GP(); - if ((total_len > MAX_ETH_MSG) || (total_len < 0)) { - total_len = MAX_ETH_MSG; - } - - p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); - - if (p == NULL) { - LWIP_DEBUGF(0, ("ecosif_input: low_level_input returned NULL\n")); - return; - } + if ((total_len > MAX_ETH_MSG) || (total_len < 0)) + total_len = MAX_ETH_MSG; - for (q = p; q != NULL; q = q->next) { - sg_list[sg_len].buf = (CYG_ADDRESS) q->payload; - sg_list[sg_len++].len = q->len; - } - (sc->funs->recv) (sc, sg_list, sg_len); - ecosif_input(netif, p); - CYGARC_HAL_RESTORE_GP(); -} - - -#define IFNAME0 'e' -#define IFNAME1 't' - - + // Allocate buffer to store received packet + p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); + if (p == NULL) { + START_CONSOLE(); + diag_printf("cannot allocate pbuf to receive packet\n"); + END_CONSOLE(); + return; + } -// -// low_level_output(): -// -// Should do the actual transmission of the packet. The packet is -// contained in the pbuf that is passed to the function. This pbuf -// might be chained.We pass the data down to the eCos hw independent -// ethernet driver -// + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (CYG_ADDRESS) q->payload; + sg_list[sg_len++].len = q->len; + } + + // Get buffers from hardware + (sc->funs->recv)(sc, sg_list, sg_len); + + // Pass received packet to the interface + netif->input(p, netif); -static err_t -low_level_output(struct netif *netif, struct pbuf *p) -{ - eth_drv_send(netif, p); - return ERR_OK; + CYGARC_HAL_RESTORE_GP(); } // -// ecosif_output(): +// DSR called from the low level driver. // -// This function is called by the TCP/IP stack when an IP packet -// should be sent. It calls the function called low_level_output() to -// do the actual transmission of the packet. -// -// -static err_t -ecosif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) +void +eth_drv_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { - // resolve hardware address, then send (or queue) packet - return etharp_output(netif, ipaddr, p); -} - - - -// -// ecosif_input(): -// This function is called when the eCos hw independent driver -// has some data to pass up to lwIP.It does it through ecosif_input. -// -static void -ecosif_input(struct netif *netif, struct pbuf *p) -{ - struct eth_hdr *ethhdr; - - ethhdr = p->payload; + struct eth_drv_sc *sc = (struct eth_drv_sc *) data; - switch (htons(ethhdr->type)) { - case ETHTYPE_IP: - LWIP_DEBUGF(0, ("ecosif_input: IP packet\n")); - etharp_ip_input(netif, p); - pbuf_header(p, -14); - netif->input(p, netif); - break; - case ETHTYPE_ARP: - LWIP_DEBUGF(0, ("ecosif_input: ARP packet\n")); - etharp_arp_input(netif, (struct eth_addr *) &netif->hwaddr, p); - break; - default: - pbuf_free(p); - break; - } - + sc->state |= ETH_DRV_NEEDS_DELIVERY; + lwip_eth_drv_dsr(); } - -err_t -ecosif_init(struct netif *netif) -{ - netif->name[0] = IFNAME0; - netif->name[1] = IFNAME1; - netif->hwaddr_len = 6; - netif->output = ecosif_output; - netif->linkoutput = low_level_output; - netif->mtu = 1500; - lwip_set_addr(netif); - return ERR_OK; -} diff --git a/packages/net/lwip_tcpip/current/ChangeLog b/packages/net/lwip_tcpip/current/ChangeLog --- a/packages/net/lwip_tcpip/current/ChangeLog +++ b/packages/net/lwip_tcpip/current/ChangeLog @@ -1,7 +1,6 @@ -2009-03-12 Sergei Gavrikov +2010-01-25 Simon Kallweit - * src/netif/slipif.c (slipif_init): in SLIP mode it quite erases - NETIF_FLAG_UP bit. Fixed. + * all: Updated to lwIP 1.3.2 2007-03-22 John Eigelaar @@ -210,25 +209,34 @@ 2004-05-04 Jani Monoses * include/lwipopts.h : Initial import of the lwIP TCP/IP stack. //=========================================================================== -// ####GPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2004 Free Software Foundation, Inc. +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 eCosCentric // -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 or (at your option) any -// later version. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the -// Free Software Foundation, Inc., 51 Franklin Street, -// Fifth Floor, Boston, MA 02110-1301, USA. -// ------------------------------------------- -// ####GPLCOPYRIGHTEND#### +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### //=========================================================================== diff --git a/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl b/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl --- a/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl +++ b/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl @@ -1,703 +1,1930 @@ -#==================================================================== +# ==================================================================== # # lwip_net.cdl # -# lwIP network stack configuration data +# lwIP networking stack configuration data. # # ==================================================================== -## ####ECOSGPLCOPYRIGHTBEGIN#### -## ------------------------------------------- -## This file is part of eCos, the Embedded Configurable Operating System. -## Copyright (C) 2004 Free Software Foundation, Inc. +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2008, 2009, 2010 Free Software Foundation ## -## eCos is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free -## Software Foundation; either version 2 or (at your option) any later -## version. +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. ## -## eCos is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -## for more details. +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. ## -## You should have received a copy of the GNU General Public License -## along with eCos; if not, write to the Free Software Foundation, Inc., -## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ## -## As a special exception, if other files instantiate templates or use -## macros or inline functions from this file, or you compile this file -## and link it with other works to produce a work based on this file, -## this file does not by itself cause the resulting work to be covered by -## the GNU General Public License. However the source code for this file -## must still be made available in accordance with section (3) of the GNU -## General Public License v2. +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. ## -## This exception does not invalidate any other reasons why a work based -## on this file might be covered by the GNU General Public License. -## ------------------------------------------- -## ####ECOSGPLCOPYRIGHTEND#### +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### # ==================================================================== ######DESCRIPTIONBEGIN#### # -# Author(s): cris@iv.ro, jani@iv.ro -# Original data: jani@iv.ro -# Contributors: -# Date: 2002-06-21 +# Author(s): Simon Kallweit +# Contributors: John Dallaway +# Date: 2008-12-01 # #####DESCRIPTIONEND#### # # ==================================================================== cdl_package CYGPKG_NET_LWIP { - display "lwIP" - description "Lightweight TCP/IP stack" - requires {(CYGPKG_LWIP_ETH == 1) || (CYGPKG_LWIP_SLIP == 1) || (CYGPKG_LWIP_PPP == 1)} - - cdl_interface CYGPKG_NET_STACK { - display "Suitable network stack implementation" - description " - Normally the interface if declared in the net common - package. However LWIP does not use that package - so we declare this interface here. Some of the device - drivers use it to decide if they should be build." + display "lwIP networking stack" + description "The lightweight TCP/IP stack." + requires CYGINT_ISO_ERRNO_CODES + + compile core/dns.c \ + core/init.c \ + core/mem.c \ + core/memp.c \ + core/netif.c \ + core/pbuf.c \ + core/raw.c \ + core/stats.c \ + core/sys.c \ + core/tcp_in.c \ + core/tcp_out.c \ + core/tcp.c \ + core/udp.c \ + core/ipv4/icmp.c \ + core/ipv4/igmp.c \ + core/ipv4/inet_chksum.c \ + core/ipv4/inet.c \ + core/ipv4/ip_addr.c \ + core/ipv4/ip_frag.c \ + core/ipv4/ip.c \ + api/api_lib.c \ + api/api_msg.c \ + api/err.c \ + api/netbuf.c \ + api/netdb.c \ + api/netifapi.c \ + api/sockets.c \ + api/tcpip.c \ + ecos/sys_arch.c + + cdl_interface CYGINT_LWIP_MODES { + display "Enabled lwIP modes" + no_define + requires CYGINT_LWIP_MODES == 1 + description " + This interface is used to force mutually exclusive selection of + the available lwIP modes." + } + + cdl_option CYGFUN_LWIP_MODE_SIMPLE { + display "Simple mode" + default_value 1 + implements CYGINT_LWIP_MODES + compile ecos/simple.c + description " + lwIP stack is configured to run in a single thread. Use this mode + if lwIP should run with a minimum of resources. Note that you can + only use the event based APIs in this mode." + } + + cdl_option CYGFUN_LWIP_MODE_SEQUENTIAL { + display "Sequential mode" + implements CYGINT_LWIP_MODES + compile ecos/sequential.c + description " + lwIP stack is configured to run in multiple threads. Use this mode + if you can spend some more resources on lwIP and you want to use + the networking stack from within other threads using the more + convenient netcomm and socket APIs." + } + + cdl_interface CYGINT_LWIP_SIO_REQUIRED { + no_define + display "Items requiring lwIP serial operations" + description " + Items requiring use of the lwIP serial operations code should + implement this interface." + } + + cdl_option CYGFUN_LWIP_SIO { + display "Serial operations support" + calculated { CYGINT_LWIP_SIO_REQUIRED > 0 } + requires CYGPKG_IO_SERIAL + compile ecos/sio.c + } + + cdl_component CYGPKG_LWIP_APIS { + display "APIs" + flavor none + no_define + description " + Configuration for Application-Programming-Interfaces." + + cdl_component CYGPKG_LWIP_RAW { + display "RAW support" + flavor bool + default_value 1 + description " + Enable application layer to hook into the IP layer itself." + + cdl_option CYGNUM_LWIP_RAW_TTL { + display "RAW Time-To-Live" + flavor data + default_value { CYGNUM_LWIP_IP_DEFAULT_TTL } + description " + Default value for Time-To-Live used by RAW packets." + } + } + + cdl_component CYGPKG_LWIP_NETIF_API { + display "Netif support" + flavor bool + default_value 0 + description " + Support for the netif API." } - cdl_interface CYGPKG_NET_STACK_INET { - display "Network stack support for IPv4" + + cdl_component CYGPKG_LWIP_NETCONN_API { + display "Netconn support" + flavor bool + default_value 1 + active_if CYGFUN_LWIP_MODE_SEQUENTIAL + requires CYGFUN_LWIP_MODE_SEQUENTIAL + description " + Support for the netconn API. Requires the \"Sequential\" mode." + } + + cdl_component CYGPKG_LWIP_SOCKET_API { + display "Socket support" + flavor bool + default_value 1 + active_if CYGFUN_LWIP_MODE_SEQUENTIAL + requires CYGFUN_LWIP_MODE_SEQUENTIAL + requires CYGPKG_LWIP_NETCONN_API + description " + Support for the socket API. Requires the \"Sequential\" mode." + + cdl_option CYGFUN_LWIP_COMPAT_SOCKETS { + display "BSD-style sockets" + flavor bool + default_value 1 + description " + Enable BSD-style sockets functions names." + } + + cdl_option CYGFUN_LWIP_POSIX_SOCKETS_IO_NAMES { + display "POSIX-style sockets" + flavor bool + default_value 1 + description " + Enable POSIX-style sockets functions names. Disable this + option if you use a POSIX operating system that uses the + same names (read, write & close)." + } + + cdl_option CYGFUN_LWIP_TCP_KEEPALIVE { + display "Keepalive options" + flavor bool + default_value 0 + description " + Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT options + processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have + to be set in seconds." + } + + cdl_option CYGFUN_LWIP_SO_RCVTIMEO { + display "SO_RCVTIMEO option" + flavor bool + default_value 0 + description " + Enable SO_RCVTIMEO processing." + } + + cdl_option CYGFUN_LWIP_SO_RCVBUF { + display "SO_RCVBUF option" + flavor bool + default_value 0 + description " + Enable SO_RCVBUF processing." + } + } + } + + cdl_component CYGPKG_LWIP_PROTOCOLS { + display "Protocols" + flavor none + no_define + description " + Configuration options for protocols." + + cdl_component CYGPKG_LWIP_ARP { + display "ARP support" + flavor bool + default_value 1 + compile netif/etharp.c + description " + Support for ARP functionality." + + cdl_option CYGNUM_LWIP_ARP_TABLE_SIZE { + display "ARP table size" + flavor data + default_value 10 + description " + Number of active MAC-IP address pairs cached." + } + + cdl_option CYGFUN_LWIP_ARP_QUEUEING { + display "ARP queueing" + flavor bool + default_value 1 + description " + Outgoing packets are queued during hardware address + resolution." + } + + cdl_option CYGFUN_LWIP_ETHARP_TRUST_IP_MAC { + display "Trust IP/MAC" + flavor bool + default_value 1 + description " + Incoming IP packets cause the ARP table to be updated with + the source MAC and IP addresses supplied in the packet. You + may want to disable this if you do not trust LAN peers to + have the correct addresses, or as a limited approach to + attempt to handle spoofing. If disabled, lwIP will need to + make a new ARP request if the peer is not already in the + ARP table, adding a little latency." + } } - cdl_interface CYGPKG_NET_STACK_INET6 { - display "Network stack support for IPv6" + cdl_component CYGPKG_LWIP_IP { + display "IP options" + flavor none + no_define + description " + Configuration options for IP protocol." + + cdl_option CYGFUN_LWIP_IP_FORWARD { + display "Support IP forwarding" + flavor bool + default_value 0 + description " + Enables the ability to forward IP packets across network + interfaces. If you are going to run lwIP on a device with + only one network interface, define this to 0." + } + + cdl_option CYGFUN_LWIP_IP_OPTIONS_ALLOWED { + display "Allow IP options" + flavor bool + default_value 1 + description " + Defines the behavior for IP options: + 0 = All packets with IP options are dropped. + 1 = IP options are allowed (but not parsed)." + } + + cdl_option CYGFUN_LWIP_IP_REASSEMBLY { + display "Support IP reassembly" + flavor bool + default_value 1 + description " + Reassemble incoming fragmented IP packets. Note that this + option does not affect outgoing packet sizes, which can be + controlled via CYGFUN_LWIP_IP_FRAG." + } + + cdl_option CYGFUN_LWIP_IP_FRAG { + display "Support IP fragmentation" + flavor bool + default_value 1 + description " + Fragment outgoing IP packets if their size exceeds MTU. + Note that this option does not affect incoming packet + sizes, which can be controlled via + CYGFUN_LWIP_IP_REASSEMBLY." + } + + cdl_option CYGNUM_LWIP_IP_REASS_MAXAGE { + display "Max reassembly time" + flavor data + default_value 3 + description " + Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, + normally) a fragmented IP packet waits for all fragments to + arrive. If not all fragments arrived in this time, the + whole packet is discarded." + } + + cdl_option CYGNUM_LWIP_IP_REASS_MAX_PBUFS { + display "Max reassembly time" + flavor data + default_value 10 + requires { CYGNUM_LWIP_PBUF_BOOL_SIZE < + CYGNUM_LWIP_IP_REASS_MAX_PBUFS } + description " + Total maximum amount of pbufs waiting to be reassembled. + Since the received pbufs are enqueued, be sure to configure + CYGNUM_LWIP_PBUF_BOOL_SIZE > CYGNUM_LWIP_IP_REASS_MAX_PBUFS + so that the stack is still able to receive packets even if + the maximum amount of fragments is enqueued for reassembly!" + } + + cdl_option CYGFUN_LWIP_IP_FRAG_USES_STATIC_BUF { + display "Use static buffer for IP fragmentation" + flavor bool + default_value 1 + description " + Use a static MTU-sized buffer for IP fragmentation. + Otherwise pbufs are allocated and reference the original + packet data to be fragmented." + } + + cdl_option CYGNUM_LWIP_IP_FRAG_MAX_MTU { + display "Max MTU size" + flavor data + default_value 1500 + active_if { CYGFUN_LWIP_IP_FRAG_USES_STATIC_BUF } + description " + Assumed max MTU on any interface for IP frag buffer." + } + + cdl_option CYGNUM_LWIP_IP_DEFAULT_TTL { + display "Default Time-To-Live" + flavor data + default_value 255 + description " + Default value for Time-To-Live used by transport layers." + } + + cdl_component CYGFUN_LWIP_IP_SOF_BROADCAST { + display "Support broadcast filter" + flavor bool + default_value 0 + description " + Use the SOF_BROADCAST field to enable broadcast filter + per pcb on udp and raw send operations. To enable + broadcast filter on recv operations, you also have to set + IP_SOF_BROADCAST_RECV=1." + + cdl_option CYGFUN_LWIP_IP_SOF_BROADCAST_RECV { + display "Receive support" + flavor bool + default_value 0 + active_if CYGFUN_LWIP_IP_SOF_BROADCAST + description " + Enable the broadcast filter on recv operations." + } + } + } + + cdl_component CYGPKG_LWIP_ICMP { + display "ICMP support" + flavor bool + default_value 1 + description " + Support for ICMP functionality. Be careful, disabling this + makes your product non-compliant to RFC1122." + + cdl_option CYGNUM_LWIP_ICMP_TTL { + display "ICMP Time-To-Live" + flavor data + default_value { CYGNUM_LWIP_IP_DEFAULT_TTL } + description " + Default value for Time-To-Live used by ICMP packets." + } + + cdl_option CYGFUN_LWIP_BROADCAST_PING { + display "Respond broadcast pings" + flavor bool + default_value 0 + description " + Respond to broadcast pings (default is unicast only)." + } + + cdl_option CYGFUN_LWIP_MULTICAST_PING { + display "Respond multicast pings" + flavor bool + default_value 0 + description " + Respond to multicast pings (default is unicast only)." + } + } + + cdl_component CYGPKG_LWIP_IGMP { + display "IGMP support" + flavor bool + default_value 1 + description " + Support for IGMP functionality." + } + + cdl_component CYGPKG_LWIP_UDP { + display "UDP support" + flavor bool + default_value 1 + requires CYGPKG_LWIP_IP + description " + Support for UDP functionality." + + cdl_option CYGFUN_LWIP_UDPLITE { + display "UDP lite" + flavor bool + default_value 1 + description " + Enable UDP lite." + } + + cdl_option CYGNUM_LWIP_UDP_TTL { + display "UDP Time-To-Live" + flavor data + default_value { CYGNUM_LWIP_IP_DEFAULT_TTL } + description " + Default value for Time-To-Live used by UDP packets." + } + } + + cdl_component CYGPKG_LWIP_TCP { + display "TCP support" + flavor bool + default_value 1 + requires CYGPKG_LWIP_IP + description " + Support for TCP functionality." + + cdl_option CYGNUM_LWIP_TCP_TTL { + display "TCP Time-To-Live" + flavor data + default_value { CYGNUM_LWIP_IP_DEFAULT_TTL } + description " + Default value for Time-To-Live used by TCP packets." + } + + cdl_option CYGNUM_LWIP_TCP_WND { + display "Receive window" + flavor data + default_value { CYGNUM_LWIP_TCP_MSS * 4 } + requires { CYGNUM_LWIP_TCP_WND >= + CYGNUM_LWIP_TCP_MSS * 2 } + description " + The size of a TCP window. This must be at least + (2 * TCP_MSS) for things to work well." + } + + cdl_option CYGNUM_LWIP_TCP_MAXRTX { + display "Segment retransmissions" + flavor data + default_value 12 + description " + Maximum number of retransmissions of data segments." + } + + cdl_option CYGNUM_LWIP_TCP_SYNMAXRTX { + display "Syn retransmissions" + flavor data + default_value 6 + description " + Maximum number of retransmissions of SYN segments." + } + + cdl_option CYGFUN_LWIP_TCP_QUEUE_OOSEQ { + display "Queue segments" + flavor bool + default_value 1 + description " + TCP will queue segments that arrive out of order. Define + to 0 if your device is low on memory." + } + + cdl_option CYGNUM_LWIP_TCP_MSS { + display "Maximum segment size" + flavor data + default_value 536 + description " + TCP Maximum segment size. (default is 128, a *very* + conservative default.) For the receive side, this MSS is + advertised to the remote side when opening a connection. + For the transmit size, this MSS sets an upper limit on the + MSS advertised by the remote host." + } + + cdl_option CYGFUN_LWIP_TCP_CALCULATE_EFF_SEND_MSS { + display "Calculate effective MSS" + flavor bool + default_value 1 + description " + \"The maximum size of a segment that TCP really sends, the + 'effective send MSS,' MUST be the smaller of the send MSS + (which reflects the available reassembly buffer size at the + remote host) and the largest size permitted by the IP + layer\" (RFC 1122) Setting this to 1 enables code that + checks TCP_MSS against the MTU of the netif used for a + connection and limits the MSS if it would be too big + otherwise." + } + + cdl_option CYGNUM_LWIP_TCP_SND_BUF { + display "Sender buffer space" + flavor data + default_value CYGNUM_LWIP_TCP_SND_QUEUELEN * CYGNUM_LWIP_TCP_MSS / 4 + requires { CYGNUM_LWIP_TCP_SND_BUF * 2 < + CYGNUM_LWIP_TCP_SND_QUEUELEN * CYGNUM_LWIP_TCP_MSS } + description " + TCP sender buffer space (bytes)." + } + + cdl_option CYGNUM_LWIP_TCP_SND_QUEUELEN { + display "Sender pbufs" + flavor data + default_value 8 + description " + TCP sender buffer space (pbufs). This must be at least as + much as (2 * TCP_SND_BUF/TCP_MSS) for things to work." + } + + cdl_option CYGNUM_LWIP_TCP_SNDLOWAT { + display "TCP writeable space" + flavor data + default_value { CYGNUM_LWIP_TCP_SND_BUF / 2 } + description " + TCP writable space (bytes). This must be less than or equal + to TCP_SND_BUF. It is the amount of space which must be + available in the TCP snd_buf for select to return writable." + } + + cdl_option CYGFUN_LWIP_TCP_LISTEN_BACKLOG { + display "Listen backlog" + flavor bool + default_value 0 + description " + Enable the backlog option for tcp listen pcb." + } + + cdl_option CYGNUM_LWIP_TCP_DEFAULT_LISTEN_BACKLOG { + display "Maximum listen backlog" + flavor data + active_if { CYGFUN_LWIP_TCP_LISTEN_BACKLOG } + default_value 255 + legal_values { 1 to 255 } + description " + The maximum allowed backlog for TCP listen netconns." + } + + cdl_option CYGFUN_LWIP_TCP_TIMESTAMPS { + display "TCP timestamp" + flavor bool + default_value 0 + description " + Support the TCP timestamp option." + } + + cdl_option CYGNUM_LWIP_TCP_WND_UPDATE_THRESHOLD { + display "Update threshold" + flavor bool + default_value { CYGNUM_LWIP_TCP_WND / 4 } + description " + Difference in window to trigger an explicit window update." + } + + cdl_option CYGFUN_LWIP_EVENT_API { + display "Event API" + flavor bool + default_value 0 + requires { !CYGFUN_LWIP_CALLBACK_API } + description " + The user defines lwip_tcp_event() to receive all events + (accept, sent, etc) that happen in the system." + } + + cdl_option CYGFUN_LWIP_CALLBACK_API { + display "Callback API" + flavor bool + default_value 1 + requires { !CYGFUN_LWIP_EVENT_API } + description " + The PCB callback function is called directly for the event." + } + } + + cdl_component CYGPKG_LWIP_DHCP { + display "DHCP support" + flavor bool + default_value 1 + requires CYGPKG_LWIP_UDP + compile core/dhcp.c + description " + Support for DHCP functionality." + + cdl_option CYGFUN_LWIP_DHCP_DOES_ARP_CHECK { + display "Check offered address" + flavor bool + default_value 1 + requires CYGPKG_LWIP_ARP + description " + Do an ARP check on the offered address." + } + } + + cdl_component CYGPKG_LWIP_AUTOIP { + display "AUTOIP support" + flavor bool + default_value 0 + requires CYGPKG_LWIP_UDP + requires CYGPKG_LWIP_ARP + compile core/ipv4/autoip.c + description " + Support for AUTOIP functionality." + + cdl_option CYGFUN_LWIP_DHCP_AUTOIP_COOP { + display "Simultaneous AUTOIP/DHCP" + flavor bool + default_value 1 + requires CYGPKG_LWIP_DHCP + description " + Allow DHCP and AUTOIP to be both enabled on the same + interface at the same time." + } + + cdl_option CYGNUM_LWIP_DHCP_AUTOIP_COOP_TRIES { + display "DHCP retries before AUTOIP" + flavor data + default_value 9 + legal_values { 1 to 10000 } + requires CYGFUN_LWIP_DHCP_AUTOIP_COOP + description " + Set to the number of DHCP DISCOVER probes that should be + sent before falling back on AUTOIP. This can be set as low + as 1 to get an AutoIP address very quickly, but you should + be prepared to handle a changing IP address when DHCP + overrides AutoIP." + } + } + + cdl_component CYGPKG_LWIP_SNMP { + display "SNMP support" + flavor bool + default_value 0 + requires CYGPKG_LWIP_UDP + compile core/snmp/asn1_dec.c \ + core/snmp/asn1_enc.c \ + core/snmp/mib_structs.c \ + core/snmp/mib2.c \ + core/snmp/msg_in.c \ + core/snmp/msg_out.c + description " + Support for SNMP functionality." + + cdl_option CYGNUM_LWIP_SNMP_CONCURRENT_REQUESTS { + display "Concurrent requests" + flavor data + default_value 1 + description " + Number of concurrent requests the module will allow. At + least one request buffer is required." + } + + cdl_option CYGNUM_LWIP_SNMP_TRAP_DESTINATIONS { + display "Trap destinations" + flavor data + default_value 1 + description " + Number of trap destinations. At least one trap destination + is required." + } + + cdl_option CYGFUN_LWIP_SNMP_PRIVATE_MIB { + display "Private MIB" + flavor bool + default_value 0 + description "" + } + + cdl_option CYGFUN_LWIP_SNMP_SAFE_REQUESTS { + display "Safe requests" + flavor bool + default_value 1 + description " + Only allow SNMP write actions that are 'safe' (e.g. + disabling netifs is not a safe action and disabled when + SNMP_SAFE_REQUESTS = 1). Unsafe requests are disabled by + default!" + } + } + + cdl_component CYGPKG_LWIP_DNS { + display "DNS support" + flavor bool + default_value 0 + requires CYGPKG_LWIP_UDP + description " + Support for DNS functionality." + + cdl_option CYGNUM_LWIP_DNS_TABLE_SIZE { + display "Table size" + flavor data + default_value 4 + description " + DNS maximum number of entries to maintain locally." + } + + cdl_option CYGNUM_LWIP_DNS_MAX_NAME_LENGTH { + display "Host name size" + flavor data + default_value 256 + description " + DNS maximum host name length supported in the name table." + } + + cdl_option CYGNUM_LWIP_DNS_MAX_SERVERS { + display "DNS servers" + flavor data + default_value 2 + description " + The maximum of DNS servers." + } + + cdl_option CYGFUN_LWIP_DNS_DOES_NAME_CHECK { + display "Name checking" + flavor bool + default_value 1 + description " + DNS do a name checking between the query and the response." + } + + cdl_option CYGNUM_LWIP_DNS_USES_STATIC_BUF { + display "Static buffer" + flavor data + default_value 1 + legal_values { 0 to 2 } + description " + DNS use a local buffer if DNS_USES_STATIC_BUF=0, a static + one if DNS_USES_STATIC_BUF=1, or a dynamic one if + DNS_USES_STATIC_BUF=2. The buffer will be of size + DNS_MSG_SIZE." + } + + cdl_option CYGNUM_LWIP_DNS_MSG_SIZE { + display "Message size" + flavor data + default_value 512 + description " + DNS message max. size. Default value is RFC compliant." + } + + cdl_component CYGPKG_LWIP_DNS_LOCAL_HOSTLIST { + display "Local host list" + flavor bool + default_value 0 + description " + Support for a local host-to-address list." + + cdl_option CYGDAT_LWIP_DNS_LOCAL_HOSTLIST_INIT { + display "Host list" + flavor data + default_value { "{{\"host1\", 0x123},\ + {\"host2\", 0x234}}" } + description " + An array of structs name/address, where address is an + u32_t in network byte order." + } + + cdl_option CYGFUN_LWIP_DNS_LOCAL_HOSTLIST_IS_DYNAMIC { + display "Dynamic host list" + flavor bool + default_value 1 + description " + If this is turned on, the local host-list can be + dynamically changed at runtime." + } + } + } + } + + cdl_component CYGPKG_LWIP_INTERFACES { + display "Interfaces" + flavor none + no_define + description " + Configuration options for support of different network interface + devices." + + cdl_interface CYGPKG_LWIP_NETIF_DEFAULT { + display "Items claiming default network interface status" + flavor data + no_define + requires { CYGPKG_LWIP_NETIF_DEFAULT <= 1 } + description " + Items which claim that a specific networking interface should + be the default interface should implement this CDL interface. + Only one networking interface can claim default networking + interface status." + } + + cdl_component CYGPKG_LWIP_NETIF { + display "Netif options" + flavor none + no_define + description " + Configuration options for netif." + + cdl_option CYGIMP_LWIP_NETIF_HOSTNAME { + display "Hostname support" + flavor bool + default_value 0 + description " + Adds a 'hostname' field to the netif structure and enables + support for retrieving the hostname via DHCP." + } + + cdl_option CYGFUN_LWIP_NETIF_STATUS_CALLBACK { + display "Status callback" + flavor bool + default_value 1 + description " + Support a callback function whenever an interface changes + its up/down status (i.e., due to DHCP IP acquistion)." + } + + cdl_option CYGFUN_LWIP_NETIF_LINK_CALLBACK { + display "Link callback" + flavor bool + default_value 0 + description " + Support a callback function from an interface whenever the + link changes (i.e., link down)." + } + + cdl_option CYGIMP_LWIP_NETIF_HWADDRHINT { + display "Cache link-layer-address hints" + flavor bool + default_value 0 + description " + Cache link-layer-address hints (e.g. table indices) in + struct netif. TCP and UDP can make use of this to prevent + scanning the ARP table for every sent packet. While this is + faster for big ARP tables or many concurrent connections, + it might be counterproductive if you have a tiny ARP table + or if there never are concurrent connections." + } + + cdl_component CYGIMP_LWIP_NETIF_LOOPBACK { + display "Loopback support" + flavor bool + default_value 0 + description " + Support sending packets with a destination IP address equal + to the netif IP address, looping them back up the stack." + + cdl_option CYGNUM_LWIP_NETIF_LOOPBACK_MAX_PBUFS { + display "PBUF queue length" + flavor data + default_value 0 + description " + Maximum number of pbufs on queue for loopback sending + for each netif (0 = disabled)." + } + } } - implements CYGPKG_NET_STACK - implements CYGPKG_NET_STACK_INET - - compile core/mem.c \ - core/memp.c \ - core/netif.c \ - core/pbuf.c \ - core/stats.c \ - core/sys.c \ - core/tcp.c \ - core/tcp_in.c \ - core/tcp_out.c \ - core/inet.c \ - core/ipv4/icmp.c \ - core/ipv4/ip.c \ - core/ipv4/ip_addr.c \ - core/ipv4/ip_frag.c \ - api/api_lib.c \ - api/api_msg.c \ - api/tcpip.c \ - api/err.c \ - api/sockets.c \ - ecos/sys_arch.c \ - ecos/init.c - - - cdl_option CYGDBG_LWIP_STATS { - display "Maintain traffic statistics" - flavor bool - default_value 0 - description " - Check this box to turn ON statistics options for lwIP." - } - - cdl_component CYGDBG_LWIP_DEBUG { - display "Support printing debug information" - flavor bool - default_value 0 - description " - Check this box to turn ON debug options for lwIP." + cdl_component CYGPKG_LWIP_ETH { + display "Ethernet support" + flavor bool + requires CYGPKG_IO + requires CYGPKG_IO_ETH_DRIVERS + requires CYGPKG_LWIP_ARP + default_value CYGPKG_IO_ETH_DRIVERS + description " + Support for ethernet devices" + + # Support static configuration of up to 4 interfaces + for { set ::channel 0 } { $::channel < 4 } { incr ::channel } { + + cdl_component CYGPKG_LWIP_ETH[set ::channel]_CONF { + display "eth[set ::channel] configuration" + flavor bool + default_value $::channel == 0 + description " + Static configuration of the ethernet device + eth[set ::channel]." - cdl_option CYGDBG_LWIP_DEBUG_TCP { - display "Control TCP debug" - flavor bool - default_value 0 - description " - Generic TCP debug switch." - - } - - } - - cdl_option CYGDBG_LWIP_ASSERTS { - display "Enable assertions" - flavor bool - default_value 0 - description " - Check this box to turn ON assertions for lwIP." - } + cdl_option CYGDAT_LWIP_ETH[set ::channel]_DEFAULT { + display "Default netif" + flavor bool + default_value $::channel == 0 + implements CYGPKG_LWIP_NETIF_DEFAULT + description " + Use this interface as the default network + interface." + } - cdl_component CYGPKG_LWIP_IPV4_CONF { - display "IPV4 netconf" - flavor none - no_define - description " - See suboptions to define gateway IP, local IP and netmask." - - cdl_option CYGDAT_LWIP_SERV_ADDR { - display "Gateway IP" - flavor data - default_value {"192,168,1,1"} - description " - Gateway's IP address." + cdl_option CYGDAT_LWIP_ETH[set ::channel]_DHCP { + display "Use DHCP" + flavor bool + default_value $::channel == 0 + requires CYGPKG_LWIP_DHCP + description " + Use DHCP to discover the IP address for this + interface." + } - } - - cdl_option CYGDAT_LWIP_MY_ADDR { - display "My IP" - flavor data - default_value {"192,168,1,222"} - description " - The IP address for this device." + cdl_component CYGPKG_LWIP_ETH[set ::channel]_NETCONF { + display "Network configuration" + flavor none + no_define + active_if CYGDAT_LWIP_ETH[set ::channel]_DHCP == 0 + description " + See suboptions to define IP address, netmask and + gateway." - } - - cdl_option CYGDAT_LWIP_NETMASK { - display "Netmask" - flavor data - default_value {"255,255,255,0"} - description " - Netmask of the local network." + cdl_option CYGDAT_LWIP_ETH[set ::channel]_NETCONF_ADDR { + display "Address" + flavor data + default_value { "192,168,1,222" } + description " + The IP address of this device." + } - } - } - - cdl_component CYGPKG_LWIP_MEM_OPTIONS { - display "Memory options" - flavor none - no_define - description " - Tunables for various aspects of memory usage throughout the stack." - - cdl_option CYGNUM_LWIP_MEM_ALIGNMENT { - display "Memory alignment" - flavor data - default_value 4 - description " - MEM_ALIGNMENT: should be set to the alignment of the CPU for which - lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, - 2 byte alignment -> define MEM_ALIGNMENT to 2." - } - cdl_option CYGNUM_LWIP_MEM_SIZE { - display "Memory size" - flavor data - default_value 4000 - description " - MEM_SIZE: the size of the heap memory. If the application will send - a lot of data that needs to be copied, this should be set high." - } - - cdl_option CYGNUM_LWIP_MEMP_NUM_PBUF { - display "Number of memp struct pbufs" - flavor data - default_value 8 - description " - MEMP_NUM_PBUF: the number of memp struct pbufs. If the application - sends a lot of data out of ROM (or other static memory), this - should be set high." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_UDP_PCB { - display "Simultaneous UDP control blocks " - flavor data - default_value 4 - description " - MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One - per active UDP 'connection'." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_TCP_PCB { - display "Simultaneous active TCP connections " - flavor data - default_value 5 - description " - MEMP_NUM_TCP_PCB: the number of simulatenously active TCP - connections." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_TCP_PCB_LISTEN { - display "Listening TCP connections" - flavor data - default_value 8 - description " - MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP - connections." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_TCP_SEG { - display "Simultaneous TCP segments queued" - flavor data - default_value 8 - description " - MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP - segments." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_SYS_TIMEOUT { - display "Simultaneous active timeouts" - flavor data - default_value CYGPKG_LWIP_DHCP ? 6 : 4 - description " - MEMP_NUM_SYS_TIMEOUT: the number of simulateously active - timeouts." - - } - cdl_component CYGPKG_LWIP_MEM_SEQ_API { - display "Sequential API settings" - flavor none - no_define - description " - The following four are used only with the sequential API and can be - set to 0 if the application only will use the raw API." - + cdl_option CYGDAT_LWIP_ETH[set ::channel]_NETCONF_NETMASK { + display "Netmask" + flavor data + default_value { "255,255,255,0" } + description " + The netmask of the local network." + } + + cdl_option CYGDAT_LWIP_ETH[set ::channel]_NETCONF_GATEWAY { + display "Gateway" + flavor data + default_value { "192,168,1,1" } + description " + The IP address of the gateway." + } + } + } + } + } + + cdl_component CYGPKG_LWIP_LOOPIF { + display "Loop interface support" + flavor bool + default_value 0 + compile netif/loopif.c + description " + Support loop interface." + + cdl_component CYGIMP_LWIP_LOOPIF_INSTANCE { + display "Default instance" + flavor bool + default_value 1 + description " + When enabled, a default loop interface is automatically + set up." + + cdl_option CYGDAT_LWIP_LOOPIF_DEFAULT { + display "Default netif" + flavor bool + default_value 0 + implements CYGPKG_LWIP_NETIF_DEFAULT + description " + Use this interface as the default network interface." + } + + cdl_option CYGDAT_LWIP_LOOPIF_ADDR { + display "Address" + flavor data + default_value { "127,0,0,1" } + description " + The IP address of this device." + } + + cdl_option CYGDAT_LWIP_LOOPIF_NETMASK { + display "Netmask" + flavor data + default_value { "255,255,255,0" } + description " + The netmask of the local network." + } + + cdl_option CYGDAT_LWIP_LOOPIF_GATEWAY { + display "Gateway" + flavor data + default_value { "127,0,0,1" } + description " + The IP address of the gateway." + } + } + } - cdl_option CYGNUM_LWIP_MEMP_NUM_NETBUF { - display "Struct netbufs" - flavor data - default_value 2 - description " - MEMP_NUM_NETBUF: the number of struct netbufs." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_NETCONN { - display "Struct netconns" - flavor data - default_value 4 - description " - MEMP_NUM_NETCONN: the number of struct netconns." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_APIMSG { - display "Struct api_msgs" - flavor data - default_value 8 - description " - MEMP_NUM_APIMSG: the number of struct api_msg, used for - communication between the TCP/IP stack and the sequential - programs." - - } - cdl_option CYGNUM_LWIP_MEMP_NUM_TCPIP_MSG { - display "Struct tcpip_msgs" - flavor data - default_value 8 - description " - MEMP_NUM_TCPIPMSG: the number of struct tcpip_msg, which is used - for sequential API communication and incoming packets. Used in - src/api/tcpip.c." - - } - } - - } - - cdl_component CYGPKG_LWIP_PBUF_OPTIONS { - display "PBUF" - flavor none - no_define - description " - Packet buffer related tunings." - - - cdl_option CYGNUM_LWIP_PBUF_POOL_SIZE { - display "PBUF pool size" - flavor data - default_value 60 - description " - PBUF_POOL_SIZE: the number of buffers in the pbuf pool." - - } - - cdl_option CYGNUM_LWIP_PBUF_POOL_BUFSIZE { - display "PBUF buffer size" - flavor data - default_value 1024 - description " - PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool." - } - - cdl_option CYGNUM_LWIP_PBUF_LINK_HLEN { - display "Allocation for a link level header" - flavor data - calculated {CYGPKG_LWIP_SLIP || CYGPKG_LWIP_PPP ? 0 : 16} - description " - PBUF_LINK_HLEN: the number of bytes that should be allocated for a - link level header." - } - } - - cdl_component CYGPKG_LWIP_TCP { - display "TCP" - flavor bool - default_value 1 - description " - Support TCP protocol." - - cdl_option CYGNUM_LWIP_TCP_TTL { - display "Time To Live" - flavor data - default_value 255 - description "" - - } - cdl_option CYGIMP_LWIP_TCP_QUEUE_OOSEQ { - display "Queue segments" - flavor bool - default_value 1 - description " - Controls if TCP should queue segments that arrive out of - order. Disable this option if your device is low on memory." - - } - cdl_option CYGNUM_LWIP_TCP_MSS { - display "Maximum segment size" - flavor data - default_value 2048 - description " - TCP Maximum segment size." - } - - cdl_option CYGNUM_LWIP_TCP_SND_BUF { - display "Sender buffer space" - flavor data - default_value 2048 - description " - TCP sender buffer space (bytes)." + cdl_component CYGPKG_LWIP_SLIP { + display "SLIP support" + flavor bool + implements CYGINT_LWIP_SIO_REQUIRED + default_value 0 + compile netif/slipif.c + description " + Support SLIP functionality." + + cdl_option CYGDAT_LWIP_SLIPIF_DEV { + display "Serial device" + flavor data + default_value { "\"/dev/ser0\"" } + description " + Serial port to use for SLIP." + } + + cdl_component CYGIMP_LWIP_SLIPIF_INSTANCE { + display "Default instance" + flavor bool + default_value 1 + description " + When enabled, a default loop interface is automatically + set up." + + cdl_option CYGDAT_LWIP_SLIPIF_DEFAULT { + display "Default netif" + flavor bool + default_value 0 + implements CYGPKG_LWIP_NETIF_DEFAULT + description " + Use this interface as the default network interface." + } + + cdl_option CYGDAT_LWIP_SLIPIF_ADDR { + display "Address" + flavor data + default_value { "172,16,1,1" } + description " + The IP address of this device." + } + + cdl_option CYGDAT_LWIP_SLIPIF_NETMASK { + display "Netmask" + flavor data + default_value { "255,255,255,0" } + description " + The netmask of the local network." + } + + cdl_option CYGDAT_LWIP_SLIPIF_GATEWAY { + display "Gateway" + flavor data + default_value { "172,16,1,2" } + description " + The IP address of the gateway." + } + } + } + + cdl_component CYGPKG_LWIP_PPP { + display "PPP support (experimental)" + flavor bool + default_value 0 + compile ecos/ppp.c \ + netif/ppp/auth.c \ + netif/ppp/chap.c \ + netif/ppp/chat.c \ + netif/ppp/chpms.c \ + netif/ppp/fsm.c \ + netif/ppp/ipcp.c \ + netif/ppp/lcp.c \ + netif/ppp/magic.c \ + netif/ppp/md5.c \ + netif/ppp/pap.c \ + netif/ppp/ppp_oe.c \ + netif/ppp/ppp.c \ + netif/ppp/randm.c \ + netif/ppp/record.c \ + netif/ppp/timesys.c \ + netif/ppp/vj.c + description " + Support PPP functionality. Current PPP support is marked + experimental as it is subject to change for alignment to the + PPP API of the upcoming lwIP 1.4.x releases." + + cdl_option CYGFUN_LWIP_PPPOE_SUPPORT { + display "PPPoE support" + flavor bool + default_value 0 + description " + Enable PPP Over Ethernet." + } + + cdl_option CYGFUN_LWIP_PPPOS_SUPPORT { + display "PPPoS support" + flavor bool + implements CYGINT_LWIP_SIO_REQUIRED + default_value 0 + description " + Enable PPP Over Serial." + } + + cdl_option CYGDAT_LWIP_PPPOS_DEV { + display "Serial device" + flavor data + default_value { "\"/dev/ser0\"" } + active_if { CYGFUN_LWIP_PPPOS_SUPPORT } + description " + Serial port to use for PPPoS." + } + + cdl_option CYGNUM_LWIP_NUM_PPP { + display "Max sessions" + flavor data + default_value 1 + description " + Max PPP sessions." + } + + cdl_option CYGFUN_LWIP_PAP_SUPPORT { + display "PAP support" + flavor bool + default_value 0 + description " + Enable PAP support." + } + + cdl_option CYGFUN_LWIP_CHAP_SUPPORT { + display "CHAP support" + flavor bool + default_value 0 + description " + Enable CHAP support." + } + + cdl_option CYGFUN_LWIP_VJ_SUPPORT { + display "VJ support" + flavor bool + default_value 0 + description " + Support VJ header compression." + } + + cdl_option CYGFUN_LWIP_MD5_SUPPORT { + display "MD5 support" + flavor bool + default_value 0 + description " + Support MD5 (see also CHAP)." + } + + cdl_option CYGFUN_LWIP_RECORD_SUPPORT { + display "Recording support" + flavor bool + default_value 0 + description " + Support for dumping incoming/outgoing records." + } + } + } + + cdl_component CYGPKG_LWIP_MEM_OPTIONS { + display "Memory options" + flavor none + no_define + description " + Tunables for various aspects of memory usage throughout the stack." + + cdl_option CYGIMP_LWIP_MEM_LIBC_MALLOC { + display "Use malloc for internal allocations" + flavor bool + default_value 0 + requires { CYGINT_ISO_MEMALLOC != 0 } + description " + Use malloc/free/realloc provided by the C-library instead of + the lwip internal allocator. Can save code size if you already + use it." + } + + cdl_option CYGIMP_LWIP_MEMP_MEM_MALLOC { + display "Use malloc for pool allocations" + flavor bool + default_value 0 + description " + Use mem_malloc/mem_free instead of the lwip pool allocator. + Especially useful with MEM_LIBC_MALLOC but handle with care + regarding execution speed and usage from interrupts!" + } + + cdl_option CYGNUM_LWIP_MEM_ALIGNMENT { + display "Memory alignment" + flavor data + default_value 4 + description " + Should be set to the alignment of the CPU." + } + + cdl_option CYGNUM_LWIP_MEM_SIZE { + display "Memory size" + flavor data + default_value { 1600 + ((CYGNUM_LWIP_NUM_PPP * 2) * 1504) } + description " + The size of the heap memory. If the application will send a lot + of data that needs to be copied, this should be set high." + } + + cdl_option CYGDBG_LWIP_MEMP_OVERFLOW_CHECK { + display "Memory pool overflow protection" + flavor data + default_value 0 + description " + Memory pool overflow protection reserves a configurable amount + of bytes before and after each memp element in every pool and + fills it with a prominent default value. + MEMP_OVERFLOW_CHECK == 0 no checking, + MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed, + MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool + every time memp_malloc() or memp_free() is called + (useful but slow!)" + } + + cdl_option CYGDBG_LWIP_MEMP_SANITY_CHECK { + display "Memory pool sanity check" + flavor bool + default_value 0 + description " + Run a sanity check after each memp_free() to make sure that + there are no cycles in the linked lists." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_PBUF { + display "Number of memp struct pbufs" + flavor data + default_value 16 + description " + The number of memp struct pbufs (used for PBUF_ROM and + PBUF_REF). If the application sends a lot of data out of ROM + (or other static memory), this should be set high." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_TCP_PCB { + display "Simultaneous active TCP connections" + flavor data + default_value 5 + active_if { CYGPKG_LWIP_TCP } + description " + The number of simulatenously active TCP connections." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_TCP_PCB_LISTEN { + display "Listening TCP connections" + flavor data + default_value 8 + active_if { CYGPKG_LWIP_TCP } + description " + The number of listening TCP connections." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_TCP_SEG { + display "Simultaneous queued TCP segments" + flavor data + default_value 16 + active_if { CYGPKG_LWIP_TCP } + description " + The number of simultaneously queued TCP segments." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_REASSDATA { + display "Simultaneous queued IP packets for reassembly" + flavor data + default_value 5 + description " + The number of simultaneously IP packets queued for reassembly + (whole packets, not fragments!)." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_ARP_QUEUE { + display "Simultaneous queued ARP requests" + flavor data + default_value 30 + active_if { CYGFUN_LWIP_ARP_QUEUEING } + description " + The number of simulateously queued outgoing packets (pbufs) + that are waiting for an ARP request (to resolve their + destination address) to finish." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_IGMP_GROUP { + display "Multicast groups" + flavor data + default_value 8 + active_if { CYGPKG_LWIP_IGMP } + description " + The number of multicast groups whose network interfaces can be + members et the same time (one per netif - allsystems group -, + plus one per netif membership)." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_CORE_SYS_TIMEOUT { + display "Simultaneous active timeouts by core modules" + flavor data + calculated { CYGPKG_LWIP_TCP + CYGFUN_LWIP_IP_REASSEMBLY + + CYGPKG_LWIP_ARP + (CYGPKG_LWIP_DHCP * 2) + + CYGPKG_LWIP_AUTOIP + CYGPKG_LWIP_IGMP + + CYGPKG_LWIP_DNS + CYGPKG_LWIP_PPP } + description " + The number of simulateously active timeouts used by the lwIP + core modules." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_USER_SYS_TIMEOUT { + display "Simultaneous active timeouts by user modules" + flavor data + default_value { 0 } + description " + The number of simulateously active timeouts used by user + modules." + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_NETBUF { + display "Struct netbufs" + flavor data + default_value 2 + description " + The number of struct netbufs. (only needed if you use the + sequential API, like api_lib.c)" + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_NETCONN { + display "Struct netconns" + flavor data + default_value 4 + description " + The number of struct netconns. (only needed if you use the + sequential API, like api_lib.c)" + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_TCPIP_MSG_API { + display "Struct tcpip_msg" + flavor data + default_value 8 + description " + The number of struct tcpip_msg, which are used for + callback/timeout API communication. (only needed if you use + tcpip.c)" + } + + cdl_option CYGNUM_LWIP_MEMP_NUM_TCPIP_MSG_INPKT { + display "Incoming struct tcpip_msg" + flavor data + default_value 8 + description " + The number of struct tcpip_msg, which are used for incoming + packets. (only needed if you use tcpip.c)" + } + + cdl_option CYGNUM_LWIP_PBUF_POOL_SIZE { + display "Pbuf pool buffers" + flavor data + default_value 16 + description " + The number of buffers in the pbuf pool." + } - } - cdl_option CYGNUM_LWIP_TCP_SND_QUEUELEN { - display "Sender pbufs" - flavor data - calculated CYGPKG_LWIP_TCP ? "4 * CYGNUM_LWIP_TCP_SND_BUF/CYGNUM_LWIP_TCP_MSS" : 0 - description " - TCP sender buffer space (pbufs). This must be at least = 2 * - TCP_SND_BUF/TCP_MSS for things to work." - - } - cdl_option CYGNUM_LWIP_TCP_WND { - display "Receive window" - flavor data - default_value 4096 - description " - TCP receive window." - - } - cdl_option CYGNUM_LWIP_TCP_MAXRTX { - display "Segment retransmissions" - flavor data - default_value 12 - description " - Maximum number of retransmissions of data segments." - - } - cdl_option CYGNUM_LWIP_TCP_SYNMAXRTX { - display "Syn retransmissions" - flavor data - default_value 4 - description " - Maximum number of retransmissions of SYN segments." - } - } - - cdl_component CYGPKG_LWIP_ARP_OPTIONS { - display "ARP" - flavor none - no_define - - cdl_option CYGNUM_LWIP_ARP_TABLE_SIZE { - display "ARP table size" - flavor data - default_value 10 - description "" - - } - } - - cdl_component CYGPKG_LWIP_IP { - display "IP" - flavor none - no_define - - cdl_option CYGFUN_LWIP_IP_FORWARD { - display "Support IP forwarding" - flavor bool - default_value 1 - description " - Enable this option if you wish to have the ability to forward - IP packets across network interfaces. If you are going to run lwIP - on a device with only one network interface, disable this option." - - } - cdl_option CYGFUN_LWIP_IP_OPTIONS { - display "Allow IP options" - flavor bool - default_value 1 - description " - If enabled, IP options are allowed (but not parsed). If - disabled, all packets with IP options are dropped." - - } - - cdl_option CYGFUN_LWIP_IP_FRAG { - display "Support IP fragmentation" - flavor bool - default_value 1 - description " - " - } - - cdl_option CYGFUN_LWIP_IP_REASS { - display "Support IP reassembly" - flavor bool - default_value 1 - description " - " - } - - } - - cdl_component CYGPKG_LWIP_ICMP_OPTIONS { - display "ICMP" - flavor none - no_define - - cdl_option CYGNUM_LWIP_ICMP_TTL { - display "ICMP Time To Live" - flavor data - default_value 255 - description "" - } - } - - cdl_component CYGPKG_LWIP_DHCP { - display "DHCP" - flavor bool - requires CYGPKG_LWIP_UDP - default_value 0 - requires { CYGNUM_LWIP_MEMP_NUM_SYS_TIMEOUT >= 6 } - description " - Provide DHCP support for initializing the IP address of network interfaces." - compile core/dhcp.c - - cdl_option CYGOPT_LWIP_DHCP_MANAGEMENT { - display "DHCP management" - flavor bool - default_value 1 - description " - If enabled then the lwIP stack automatically calls dhcp_start(), - dhcp_fine_tmr() and dhcp_coarse_tmr(). The DHCP stuff is handled - in the TCP/IP thread. If this causes trouble on high traffic loads - or if the application need to be aware of the DHCP state then it - is better to disable this option. In this case managing the DHCP - state in an application aware thread is recommended." + cdl_option CYGNUM_LWIP_VARMEMPOOL_SIZE { + display "Size of variable memory pool" + flavor data + default_value 2048 + description " + Memory required to hold semaphore, mbox and thread structures + is allocated from this memory pool." + } + + cdl_option CYGNUM_LWIP_PBUF_LINK_HLEN { + display "Allocation for a link level header" + flavor data + default_value { (CYGPKG_LWIP_ETH) ? 14 : 0 } + requires { CYGNUM_LWIP_PBUF_LINK_HLEN >= + (CYGPKG_LWIP_ETH) ? 14 : 0 } + description " + The number of bytes that should be allocated for a link level + header. The default is 14, the standard value for Ethernet." } - cdl_option CYGOPT_LWIP_DHCP_DOES_ARP_CHECK { - display "Check offered address" - flavor bool - default_value 1 - description " - Enable this option if you want to do an ARP check on the offered address - (recommended)." - } - } - - cdl_component CYGFUN_LWIP_LOOPIF { - display "Support loop interface (127.0.0.1)" - flavor bool - default_value 1 - compile netif/loopif.c - } + cdl_option CYGNUM_LWIP_PBUF_POOL_BUFSIZE { + display "PBUF buffer size" + flavor data + default_value { CYGNUM_LWIP_TCP_MSS + 40 + CYGNUM_LWIP_PBUF_LINK_HLEN } + description " + The size of each pbuf in the pbuf pool. The default is designed + to accomodate single full size TCP frame in one pbuf, including + TCP_MSS, IP header, and link header." + } + } + + cdl_component CYGPKG_LWIP_THREADS { + display "Thread options" + flavor none + no_define + active_if CYGFUN_LWIP_MODE_SEQUENTIAL + + cdl_component CYGPKG_LWIP_THREAD_TCPIP { + display "TCP/IP thread" + flavor none + no_define + description " + The TCP/IP thread is handling the central message queue in the + lwIP stack. The name is misleading, as it does not only handle + TCP/IP, but all of the stack." + + cdl_option CYGNUM_LWIP_THREAD_TCPIP_STACKSIZE { + display "Stack size" + flavor data + default_value 1024 + } + + cdl_option CYGNUM_LWIP_THREAD_TCPIP_PRIORITY { + display "Thread priority" + flavor data + default_value 8 + } + } + + cdl_component CYGPKG_LWIP_THREAD_ETH { + display "Ethernet thread" + flavor none + no_define + active_if { CYGPKG_LWIP_ETH } + description " + The ethernet thread is responsible for receiving data packets + from ethernet devices and queueing them to be processed in the + TCP/IP thread." - cdl_component CYGPKG_LWIP_ETH { - display "Ethernet support" - flavor bool - requires CYGPKG_IO_ETH_DRIVERS - default_value 1 - description "Ethernet support" - compile netif/etharp.c - - cdl_option CYGNUM_LWIP_ETH_THREAD_PRIORITY { - display "ethernet input thread priority" - flavor data - default_value 6 - description "Priority of the thernet input thread" - } - - } - - cdl_option CYGNUM_LWIP_NETWORK_THREAD_PRIORITY { - display "Network thread priority" - flavor data - default_value 7 - description "Priority of the lwIP network thread.This thread handles all API messages and - network packets." - } + cdl_option CYGNUM_LWIP_THREAD_ETH_STACKSIZE { + display "Stack size" + flavor data + default_value 1024 + } + + cdl_option CYGNUM_LWIP_THREAD_ETH_PRIORITY { + display "Thread priority" + flavor data + default_value 4 + } + } + + cdl_component CYGPKG_LWIP_THREAD_SLIP { + display "SLIP thread" + flavor none + no_define + active_if { CYGPKG_LWIP_SLIP } + description " + The SLIP thread is responsible for reading data from the SLIP + serial device and queue them to be processed in the TCP/IP + thread." + + cdl_option CYGNUM_LWIP_THREAD_SLIP_STACKSIZE { + display "Stack size" + flavor data + default_value 1024 + } + + cdl_option CYGNUM_LWIP_THREAD_SLIP_PRIORITY { + display "Thread priority" + flavor data + default_value 8 + } + } + + cdl_component CYGPKG_LWIP_THREAD_PPPOS { + display "PPPoS thread" + flavor none + no_define + active_if { CYGFUN_LWIP_PPPOS_SUPPORT } + description " + The PPPoS thread is responsible for reading data from the PPP + serial device and queue them to be processed in the TCP/IP + thread." + + cdl_option CYGNUM_LWIP_THREAD_PPPOS_STACKSIZE { + display "Stack size" + flavor data + default_value 1024 + } + + cdl_option CYGNUM_LWIP_THREAD_PPPOS_PRIORITY { + display "Thread priority" + flavor data + default_value 8 + } + } + } - cdl_component CYGPKG_LWIP_SLIP { - display "SLIP" - flavor bool - requires CYGPKG_IO_SERIAL_DEVICES - default_value 0 - description "IP over Serial Line" - compile netif/slipif.c ecos/sio.c - - cdl_option CYGNUM_LWIP_SLIPIF_THREAD_PRIORITY { - display "SLIP thread priority" - flavor data - default_value 8 - description "Priority of the SLIP input thread" - } - - cdl_option CYGDAT_LWIP_SLIP_DEV { - display "Serial device" - flavor data - default_value {"\"/dev/ser0\""} - description " - Which serial port to use SLIP on." - } - } - - cdl_component CYGPKG_LWIP_PPP { - display "PPP" - flavor bool - requires CYGPKG_IO_SERIAL_DEVICES - default_value 0 - description "The Point-to-Point Protocol" - compile netif/ppp/ppp.c \ - netif/ppp/auth.c \ - netif/ppp/chpms.c \ - netif/ppp/fsm.c \ - netif/ppp/ipcp.c \ - netif/ppp/lcp.c \ - netif/ppp/magic.c \ - netif/ppp/md5.c \ - netif/ppp/randm.c \ - netif/ppp/vj.c \ - ecos/sio.c - + cdl_component CYGPKG_LWIP_CHECKSUMS { + display "Checksum generation/checking" + flavor none + no_define + description " + Options for checksum generation and checking." + + cdl_option CYGIMP_LWIP_CHECKSUM_GEN_IP { + display "Generate IP checksums" + flavor bool + default_value 1 + description " + Generate checksums in software for outgoing IP packets." + } + + cdl_option CYGIMP_LWIP_CHECKSUM_GEN_UDP { + display "Generate UDP checksums" + flavor bool + default_value 1 + description " + Generate checksums in software for outgoing UDP packets." + } + + cdl_option CYGIMP_LWIP_CHECKSUM_GEN_TCP { + display "Generate TCP checksums" + flavor bool + default_value 1 + description " + Generate checksums in software for outgoing TCP packets." + } - cdl_option CYGIMP_LWIP_PPP_PAP_AUTH { - display "Support PAP authentication" - flavor bool - default_value 1 - compile netif/ppp/pap.c - } - - cdl_option CYGIMP_LWIP_PPP_CHAP_AUTH { - display "Support CHAP authentication" - flavor bool - default_value 1 - compile netif/ppp/chap.c - } - - cdl_option CYGDAT_LWIP_PPP_DEV { - display "Serial device for PPP" - flavor data - default_value {"\"/dev/ser0\""} - description " - Which serial port to use PPP on." - } - - cdl_option CYGNUM_LWIP_PPP_THREAD_PRIORITY { - display "PPP main thread priority" - flavor data - default_value 8 - description "Priority of the PPP input thread" - } - } - - cdl_component CYGPKG_LWIP_UDP { - display "UDP" - flavor bool - default_value 1 - description "Support UDP protocol." - compile core/udp.c - - cdl_option CYGNUM_LWIP_UDP_TTL { - display "Time To Live" - flavor data - default_value 255 - description "" - } - } - - cdl_option CYGFUN_LWIP_RAW { - display "Enable RAW socket support" - flavor bool - default_value 1 - description "" - compile core/raw.c - } - - cdl_option CYGFUN_LWIP_COMPAT_SOCKETS { - display "Provide compatible socket API" - flavor bool - default_value 1 - description " - The lwIP socket API uses defines to map the lwip socket functions - (lwip_accept(), lwip_bind(), lwip_listen()...) to BSD like names - (accept(), bind(), listen()...). If this causes trouble or naming - conficts for your application, then disable this option" + cdl_option CYGIMP_LWIP_CHECKSUM_CHECK_IP { + display "Check IP checksums" + flavor bool + default_value 1 + description " + Check checksums in software for incoming IP packets." + } + + cdl_option CYGIMP_LWIP_CHECKSUM_CHECK_UDP { + display "Check UDP checksums" + flavor bool + default_value 1 + description " + Check checksums in software for incoming UDP packets." + } + + cdl_option CYGIMP_LWIP_CHECKSUM_CHECK_TCP { + display "Check TCP checksums" + flavor bool + default_value 1 + description " + Check checksums in software for incoming TCP packets." + } + } + + cdl_component CYGPKG_LWIP_STATS { + display "Traffic statistics" + flavor bool + default_value 0 + description " + Enable statistics collection in lwip_stats." + + cdl_option CYGVAR_LWIP_LINK_STATS { + display "Link stats" + flavor bool + default_value 1 + } + + cdl_option CYGVAR_LWIP_ETHARP_STATS { + display "Etharp stats" + flavor bool + default_value 1 + } + + cdl_option CYGVAR_LWIP_IP_STATS { + display "IP stats" + flavor bool + default_value 1 + } + + cdl_option CYGVAR_LWIP_IPFARG_STATS { + display "IP fragmentation stats" + flavor bool + default_value 1 + active_if { CYGFUN_LWIP_IP_REASSEMBLY || + CYGFUN_LWIP_IP_FRAG } + } + + cdl_option CYGVAR_LWIP_ICMP_STATS { + display "ICMP stats" + flavor bool + default_value 1 + active_if { CYGPKG_LWIP_ICMP } + } + + cdl_option CYGVAR_LWIP_IGMP_STATS { + display "IGMP stats" + flavor bool + default_value 1 + active_if { CYGPKG_LWIP_IGMP } + } + + cdl_option CYGVAR_LWIP_UDP_STATS { + display "UDP stats" + flavor bool + default_value 1 + active_if { CYGPKG_LWIP_UDP } + } + + cdl_option CYGVAR_LWIP_TCP_STATS { + display "TCP stats" + flavor bool + default_value 1 + active_if { CYGPKG_LWIP_TCP } + } + + cdl_option CYGVAR_LWIP_MEM_STATS { + display "Memory stats" + flavor bool + default_value 1 + } + + cdl_option CYGVAR_LWIP_MEMP_STATS { + display "Memory pool stats" + flavor bool + default_value 1 + } + + cdl_option CYGVAR_LWIP_SYS_STATS { + display "System stats" + flavor bool + default_value 1 + } + } + + cdl_option CYGFUN_LWIP_SHOW_NETIF_CONFIG { + display "Show netif configuration" + flavor bool + default_value 1 + requires CYGFUN_LWIP_NETIF_STATUS_CALLBACK + description " + Shows the netif configuration on the diagnostic output." } - cdl_component CYGPKG_LWIP_APP_MEM_OPTIONS { - display "Memory options for apps" - flavor none - no_define - description "Memory options for applications." - - cdl_option CYGNUM_LWIP_VARMEMPOOL_SIZE { - display "Size of variable memory pool" - flavor data - default_value 2048 - description " - Memory required to hold semaphore, mbox and thread structures - are allocated from this memory pool. - " - } - - + cdl_component CYGDBG_LWIP_DEBUG { + display "Debugging" + flavor bool + default_value 0 + description " + Configuration options for debug output." + + cdl_component CYGDBG_LWIP_DEBUG_OPTIONS { + display "Debug options" + flavor none + + cdl_option CYGDBG_LWIP_DEBUG_LEVEL { + display "Log level" + flavor data + default_value 0 + legal_values { 0 to 3 } + description " + Sets the minimum log level needed to for log output. + 0 = Info, 1 = Warning, 2 = Serious, 3 = Severe" + } + + cdl_option CYGDBG_LWIP_DEBUG_TRACE { + display "Trace messages" + flavor bool + default_value 1 + description " + Log state tracing messages (to follow module flow)." + } + + cdl_option CYGDBG_LWIP_DEBUG_STATE { + display "State messages" + flavor bool + default_value 1 + description " + Log state debug messages (to follow module states)." + } + + cdl_option CYGDBG_LWIP_DEBUG_FRESH { + display "Fresh messages" + flavor bool + default_value 1 + description " + Log messages from newly added code, not thoroughly tested + yet." + } + } + + cdl_option CYGDBG_LWIP_DEBUG_ETHARP { + display "Etharp debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_NETIF { + display "Netif debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_PBUF { + display "PBUF debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_API { + display "API debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_SOCKETS { + display "Sockets debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_ICMP { + display "ICMP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_IGMP { + display "IGMP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_IP { + display "IP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_RAW { + display "RAW debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_MEM { + display "Memory debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_SYS { + display "System debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_TCP { + display "TCP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_UDP { + display "UDP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_TCPIP { + display "TCPIP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_PPP { + display "PPP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_SLIP { + display "SLIP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_DHCP { + display "DHCP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_AUTOIP { + display "AUTOIP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_SNMP { + display "SNMP debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_DNS { + display "DNS debugging" + flavor bool + default_value 1 + } + + cdl_option CYGDBG_LWIP_DEBUG_SIO { + display "SIO debugging" + flavor bool + default_value 1 + } + } + + cdl_component CYGPKG_NET_LWIP_TESTS { + display "lwIP tests" + flavor data + no_define + calculated { "tests/httpd_sequential tests/httpd_simple " . + "tests/nc_test_slave tests/ppp " . + "tests/socket tests/sys_timeout tests/tcpecho " . + "tests/udpecho" } + description " + This option specifies the set of tests for lwIP. They show the + usage of the raw, the sequential and the BSD socket compatible APIs" + + cdl_component CYGPKG_NET_LWIP_PPP_TEST { + display "PPP test options" + flavor none + no_define + + cdl_option CYGDAT_NET_LWIP_PPP_TEST_APN { + display "APN" + flavor data + default_value { "\"gprs.swisscom.ch\"" } + description " + Access Point Name used to open PPP connection." + } + + cdl_option CYGDAT_NET_LWIP_PPP_TEST_NUMBER { + display "Number" + flavor data + default_value { "\"*99#\"" } + description " + Dial number used to open PPP connection." + } + + cdl_option CYGDAT_NET_LWIP_PPP_TEST_USERNAME { + display "Username" + flavor data + default_value { "\"gprs\"" } + description " + Username used for PPP authentication." + } + + cdl_option CYGDAT_NET_LWIP_PPP_TEST_PASSWORD { + display "Password" + flavor data + default_value { "\"gprs\"" } + description " + Password used for PPP authentication." + } + + cdl_option CYGDAT_NET_LWIP_PPP_TEST_HOST { + display "Host to ping" + flavor data + default_value { "\"www.ping.ch\"" } + description " + Hostname for DNS resolve and ping." + } + } + } - cdl_option CYGNUM_LWIP_APP_THREADS { - display "Number of network threads in application" - flavor data - default_value 1 - description " - At startup at least two lwIP threads are created:the polling(input) thread - and the TCP/IP (output) thread.Additionally your application creates one - or more threads. - Set this option to the maximum number of threads you will create through - sys_thread_new().Threads which you create through cyg_thread_create() - are not lwIP threads and don't count.This number is needed so that enough - static memory is reserved for stack space. - " - } - - cdl_option CYGNUM_LWIP_THREAD_STACK_SIZE { - display "Size of per thread stack in lwIP" - flavor data - default_value 4096 - description " - Since stack space for threads needs to be statically allocated you can - specify the amount of memory to use for each network thread. - " - } - } - - - cdl_option CYGPKG_NET_LWIP_TESTS { - display "Some lwIP tests" - flavor data - no_define - calculated { - "tests/tcpecho tests/udpecho tests/httpd tests/socket tests/nc_test_slave tests/sys_timeout" - } - description " - This option specifies the set of tests for lwIP.They show the usage of - the raw, the sequential and the BSD socket compatible APIs" - } - + cdl_component CYGPKG_LWIP_OPTIONS { + display "lwIP build options" + flavor none + no_define + + cdl_option CYGPKG_LWIP_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for building the + lwIP package. These flags are used in addition to the set of + global flags." + } + + cdl_option CYGPKG_LWIP_CFLAGS_REMOVE { + display "Suppressed compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for building the + lwIP package. These flags are removed from the set of global + flags if present." + } + } } diff --git a/packages/net/lwip_tcpip/current/doc/contrib.txt b/packages/net/lwip_tcpip/current/doc/contrib.txt new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/doc/contrib.txt @@ -0,0 +1,63 @@ +1 Introduction + +This document describes some guidelines for people participating +in lwIP development. + +2 How to contribute to lwIP + +Here is a short list of suggestions to anybody working with lwIP and +trying to contribute bug reports, fixes, enhancements, platform ports etc. +First of all as you may already know lwIP is a volunteer project so feedback +to fixes or questions might often come late. Hopefully the bug and patch tracking +features of Savannah help us not lose users' input. + +2.1 Source code style: + +1. do not use tabs. +2. indentation is two spaces per level (i.e. per tab). +3. end debug messages with a trailing newline (\n). +4. one space between keyword and opening bracket. +5. no space between function and opening bracket. +6. one space and no newline before opening curly braces of a block. +7. closing curly brace on a single line. +8. spaces surrounding assignment and comparisons. +9. don't initialize static and/or global variables to zero, the compiler takes care of that. +10. use current source code style as further reference. + +2.2 Source code documentation style: + +1. JavaDoc compliant and Doxygen compatible. +2. Function documentation above functions in .c files, not .h files. + (This forces you to synchronize documentation and implementation.) +3. Use current documentation style as further reference. + +2.3 Bug reports and patches: + +1. Make sure you are reporting bugs or send patches against the latest + sources. (From the latest release and/or the current CVS sources.) +2. If you think you found a bug make sure it's not already filed in the + bugtracker at Savannah. +3. If you have a fix put the patch on Savannah. If it is a patch that affects + both core and arch specific stuff please separate them so that the core can + be applied separately while leaving the other patch 'open'. The prefered way + is to NOT touch archs you can't test and let maintainers take care of them. + This is a good way to see if they are used at all - the same goes for unix + netifs except tapif. +4. Do not file a bug and post a fix to it to the patch area. Either a bug report + or a patch will be enough. + If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area. +5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two) + can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded + as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead + for reporting a compiler warning fix. +6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other + trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you + change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than + if it's not to the point and long :) so the chances for it to be applied are greater. + +2.4 Platform porters: + +1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and + you think it could benefit others[1] you might want discuss this on the mailing list. You + can also ask for CVS access to submit and maintain your port in the contrib CVS module. + \ No newline at end of file diff --git a/packages/net/lwip_tcpip/current/doc/rawapi.txt b/packages/net/lwip_tcpip/current/doc/rawapi.txt new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/doc/rawapi.txt @@ -0,0 +1,478 @@ +Raw TCP/IP interface for lwIP + +Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons + +lwIP provides three Application Program's Interfaces (APIs) for programs +to use for communication with the TCP/IP code: +* low-level "core" / "callback" or "raw" API. +* higher-level "sequential" API. +* BSD-style socket API. + +The sequential API provides a way for ordinary, sequential, programs +to use the lwIP stack. It is quite similar to the BSD socket API. The +model of execution is based on the blocking open-read-write-close +paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP +code and the application program must reside in different execution +contexts (threads). + +The socket API is a compatibility API for existing applications, +currently it is built on top of the sequential API. It is meant to +provide all functions needed to run socket API applications running +on other platforms (e.g. unix / windows etc.). However, due to limitations +in the specification of this API, there might be incompatibilities +that require small modifications of existing programs. + +** Threading + +lwIP started targeting single-threaded environments. When adding multi- +threading support, instead of making the core thread-safe, another +approach was chosen: there is one main thread running the lwIP core +(also known as the "tcpip_thread"). The raw API may only be used from +this thread! Application threads using the sequential- or socket API +communicate with this main thread through message passing. + + As such, the list of functions that may be called from + other threads or an ISR is very limited! Only functions + from these API header files are thread-safe: + - api.h + - netbuf.h + - netdb.h + - netifapi.h + - sockets.h + - sys.h + + Additionaly, memory (de-)allocation functions may be + called from multiple threads (not ISR!) with NO_SYS=0 + since they are protected by SYS_LIGHTWEIGHT_PROT and/or + semaphores. + + Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1 + and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1, + pbuf_free() may also be called from another thread or + an ISR (since only then, mem_free - for PBUF_RAM - may + be called from an ISR: otherwise, the HEAP is only + protected by semaphores). + + +** The remainder of this document discusses the "raw" API. ** + +The raw TCP/IP interface allows the application program to integrate +better with the TCP/IP code. Program execution is event based by +having callback functions being called from within the TCP/IP +code. The TCP/IP code and the application program both run in the same +thread. The sequential API has a much higher overhead and is not very +well suited for small systems since it forces a multithreaded paradigm +on the application. + +The raw TCP/IP interface is not only faster in terms of code execution +time but is also less memory intensive. The drawback is that program +development is somewhat harder and application programs written for +the raw TCP/IP interface are more difficult to understand. Still, this +is the preferred way of writing applications that should be small in +code size and memory usage. + +Both APIs can be used simultaneously by different application +programs. In fact, the sequential API is implemented as an application +program using the raw TCP/IP interface. + +--- Callbacks + +Program execution is driven by callbacks. Each callback is an ordinary +C function that is called from within the TCP/IP code. Every callback +function is passed the current TCP or UDP connection state as an +argument. Also, in order to be able to keep program specific state, +the callback functions are called with a program specified argument +that is independent of the TCP/IP state. + +The function for setting the application connection state is: + +- void tcp_arg(struct tcp_pcb *pcb, void *arg) + + Specifies the program specific state that should be passed to all + other callback functions. The "pcb" argument is the current TCP + connection control block, and the "arg" argument is the argument + that will be passed to the callbacks. + + +--- TCP connection setup + +The functions used for setting up connections is similar to that of +the sequential API and of the BSD socket API. A new TCP connection +identifier (i.e., a protocol control block - PCB) is created with the +tcp_new() function. This PCB can then be either set to listen for new +incoming connections or be explicitly connected to another host. + +- struct tcp_pcb *tcp_new(void) + + Creates a new connection identifier (PCB). If memory is not + available for creating the new pcb, NULL is returned. + +- err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port) + + Binds the pcb to a local IP address and port number. The IP address + can be specified as IP_ADDR_ANY in order to bind the connection to + all local IP addresses. + + If another connection is bound to the same port, the function will + return ERR_USE, otherwise ERR_OK is returned. + +- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb) + + Commands a pcb to start listening for incoming connections. When an + incoming connection is accepted, the function specified with the + tcp_accept() function will be called. The pcb will have to be bound + to a local port with the tcp_bind() function. + + The tcp_listen() function returns a new connection identifier, and + the one passed as an argument to the function will be + deallocated. The reason for this behavior is that less memory is + needed for a connection that is listening, so tcp_listen() will + reclaim the memory needed for the original connection and allocate a + new smaller memory block for the listening connection. + + tcp_listen() may return NULL if no memory was available for the + listening connection. If so, the memory associated with the pcb + passed as an argument to tcp_listen() will not be deallocated. + +- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) + + Same as tcp_listen, but limits the number of outstanding connections + in the listen queue to the value specified by the backlog argument. + To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h. + +- void tcp_accepted(struct tcp_pcb *pcb) + + Inform lwIP that an incoming connection has been accepted. This would + usually be called from the accept callback. This allows lwIP to perform + housekeeping tasks, such as allowing further incoming connections to be + queued in the listen backlog. + +- void tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)) + + Specified the callback function that should be called when a new + connection arrives on a listening connection. + +- err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + + Sets up the pcb to connect to the remote host and sends the + initial SYN segment which opens the connection. + + The tcp_connect() function returns immediately; it does not wait for + the connection to be properly setup. Instead, it will call the + function specified as the fourth argument (the "connected" argument) + when the connection is established. If the connection could not be + properly established, either because the other host refused the + connection or because the other host didn't answer, the "err" + callback function of this pcb (registered with tcp_err, see below) + will be called. + + The tcp_connect() function can return ERR_MEM if no memory is + available for enqueueing the SYN segment. If the SYN indeed was + enqueued successfully, the tcp_connect() function returns ERR_OK. + + +--- Sending TCP data + +TCP data is sent by enqueueing the data with a call to +tcp_write(). When the data is successfully transmitted to the remote +host, the application will be notified with a call to a specified +callback function. + +- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len, + u8_t copy) + + Enqueues the data pointed to by the argument dataptr. The length of + the data is passed as the len parameter. The copy argument is either + 0 or 1 and indicates whether the new memory should be allocated for + the data to be copied into. If the argument is 0, no new memory + should be allocated and the data should only be referenced by + pointer. + + The tcp_write() function will fail and return ERR_MEM if the length + of the data exceeds the current send buffer size or if the length of + the queue of outgoing segment is larger than the upper limit defined + in lwipopts.h. The number of bytes available in the output queue can + be retrieved with the tcp_sndbuf() function. + + The proper way to use this function is to call the function with at + most tcp_sndbuf() bytes of data. If the function returns ERR_MEM, + the application should wait until some of the currently enqueued + data has been successfully received by the other host and try again. + +- void tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)) + + Specifies the callback function that should be called when data has + successfully been received (i.e., acknowledged) by the remote + host. The len argument passed to the callback function gives the + amount bytes that was acknowledged by the last acknowledgment. + + +--- Receiving TCP data + +TCP data reception is callback based - an application specified +callback function is called when new data arrives. When the +application has taken the data, it has to call the tcp_recved() +function to indicate that TCP can advertise increase the receive +window. + +- void tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)) + + Sets the callback function that will be called when new data + arrives. The callback function will be passed a NULL pbuf to + indicate that the remote host has closed the connection. If + there are no errors and the callback function is to return + ERR_OK, then it must free the pbuf. Otherwise, it must not + free the pbuf so that lwIP core code can store it. + +- void tcp_recved(struct tcp_pcb *pcb, u16_t len) + + Must be called when the application has received the data. The len + argument indicates the length of the received data. + + +--- Application polling + +When a connection is idle (i.e., no data is either transmitted or +received), lwIP will repeatedly poll the application by calling a +specified callback function. This can be used either as a watchdog +timer for killing connections that have stayed idle for too long, or +as a method of waiting for memory to become available. For instance, +if a call to tcp_write() has failed because memory wasn't available, +the application may use the polling functionality to call tcp_write() +again when the connection has been idle for a while. + +- void tcp_poll(struct tcp_pcb *pcb, u8_t interval, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb)) + + Specifies the polling interval and the callback function that should + be called to poll the application. The interval is specified in + number of TCP coarse grained timer shots, which typically occurs + twice a second. An interval of 10 means that the application would + be polled every 5 seconds. + + +--- Closing and aborting connections + +- err_t tcp_close(struct tcp_pcb *pcb) + + Closes the connection. The function may return ERR_MEM if no memory + was available for closing the connection. If so, the application + should wait and try again either by using the acknowledgment + callback or the polling functionality. If the close succeeds, the + function returns ERR_OK. + + The pcb is deallocated by the TCP code after a call to tcp_close(). + +- void tcp_abort(struct tcp_pcb *pcb) + + Aborts the connection by sending a RST (reset) segment to the remote + host. The pcb is deallocated. This function never fails. + +If a connection is aborted because of an error, the application is +alerted of this event by the err callback. Errors that might abort a +connection are when there is a shortage of memory. The callback +function to be called is set using the tcp_err() function. + +- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, + err_t err)) + + The error callback function does not get the pcb passed to it as a + parameter since the pcb may already have been deallocated. + + +--- Lower layer TCP interface + +TCP provides a simple interface to the lower layers of the +system. During system initialization, the function tcp_init() has +to be called before any other TCP function is called. When the system +is running, the two timer functions tcp_fasttmr() and tcp_slowtmr() +must be called with regular intervals. The tcp_fasttmr() should be +called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and +tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. + + +--- UDP interface + +The UDP interface is similar to that of TCP, but due to the lower +level of complexity of UDP, the interface is significantly simpler. + +- struct udp_pcb *udp_new(void) + + Creates a new UDP pcb which can be used for UDP communication. The + pcb is not active until it has either been bound to a local address + or connected to a remote address. + +- void udp_remove(struct udp_pcb *pcb) + + Removes and deallocates the pcb. + +- err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port) + + Binds the pcb to a local address. The IP-address argument "ipaddr" + can be IP_ADDR_ANY to indicate that it should listen to any local IP + address. The function currently always return ERR_OK. + +- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port) + + Sets the remote end of the pcb. This function does not generate any + network traffic, but only set the remote address of the pcb. + +- err_t udp_disconnect(struct udp_pcb *pcb) + + Remove the remote end of the pcb. This function does not generate + any network traffic, but only removes the remote address of the pcb. + +- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) + + Sends the pbuf p. The pbuf is not deallocated. + +- void udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + struct ip_addr *addr, + u16_t port), + void *recv_arg) + + Specifies a callback function that should be called when a UDP + datagram is received. + + +--- System initalization + +A truly complete and generic sequence for initializing the lwip stack +cannot be given because it depends on the build configuration (lwipopts.h) +and additional initializations for your runtime environment (e.g. timers). + +We can give you some idea on how to proceed when using the raw API. +We assume a configuration using a single Ethernet netif and the +UDP and TCP transport layers, IPv4 and the DHCP client. + +Call these functions in the order of appearance: + +- stats_init() + + Clears the structure where runtime statistics are gathered. + +- sys_init() + + Not of much use since we set the NO_SYS 1 option in lwipopts.h, + to be called for easy configuration changes. + +- mem_init() + + Initializes the dynamic memory heap defined by MEM_SIZE. + +- memp_init() + + Initializes the memory pools defined by MEMP_NUM_x. + +- pbuf_init() + + Initializes the pbuf memory pool defined by PBUF_POOL_SIZE. + +- etharp_init() + + Initializes the ARP table and queue. + Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval + after this initialization. + +- ip_init() + + Doesn't do much, it should be called to handle future changes. + +- udp_init() + + Clears the UDP PCB list. + +- tcp_init() + + Clears the TCP PCB list and clears some internal TCP timers. + Note: you must call tcp_fasttmr() and tcp_slowtmr() at the + predefined regular intervals after this initialization. + +- netif_add(struct netif *netif, struct ip_addr *ipaddr, + struct ip_addr *netmask, struct ip_addr *gw, + void *state, err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) + + Adds your network interface to the netif_list. Allocate a struct + netif and pass a pointer to this structure as the first argument. + Give pointers to cleared ip_addr structures when using DHCP, + or fill them with sane numbers otherwise. The state pointer may be NULL. + + The init function pointer must point to a initialization function for + your ethernet netif interface. The following code illustrates it's use. + + err_t netif_if_init(struct netif *netif) + { + u8_t i; + + for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i]; + init_my_eth_device(); + return ERR_OK; + } + + For ethernet drivers, the input function pointer must point to the lwip + function ethernet_input() declared in "netif/etharp.h". Other drivers + must use ip_input() declared in "lwip/ip.h". + +- netif_set_default(struct netif *netif) + + Registers the default network interface. + +- netif_set_up(struct netif *netif) + + When the netif is fully configured this function must be called. + +- dhcp_start(struct netif *netif) + + Creates a new DHCP client for this interface on the first call. + Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at + the predefined regular intervals after starting the client. + + You can peek in the netif->dhcp struct for the actual DHCP status. + + +--- Optimalization hints + +The first thing you want to optimize is the lwip_standard_checksum() +routine from src/core/inet.c. You can override this standard +function with the #define LWIP_CHKSUM . + +There are C examples given in inet.c or you might want to +craft an assembly function for this. RFC1071 is a good +introduction to this subject. + +Other significant improvements can be made by supplying +assembly or inline replacements for htons() and htonl() +if you're using a little-endian architecture. +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) +#define LWIP_PLATFORM_HTONL(x) + +Check your network interface driver if it reads at +a higher speed than the maximum wire-speed. If the +hardware isn't serviced frequently and fast enough +buffer overflows are likely to occur. + +E.g. when using the cs8900 driver, call cs8900if_service(ethif) +as frequently as possible. When using an RTOS let the cs8900 interrupt +wake a high priority task that services your driver using a binary +semaphore or event flag. Some drivers might allow additional tuning +to match your application and network. + +For a production release it is recommended to set LWIP_STATS to 0. +Note that speed performance isn't influenced much by simply setting +high values to the memory options. diff --git a/packages/net/lwip_tcpip/current/doc/savannah.txt b/packages/net/lwip_tcpip/current/doc/savannah.txt new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/doc/savannah.txt @@ -0,0 +1,135 @@ +Daily Use Guide for using Savannah for lwIP + +Table of Contents: + +1 - Obtaining lwIP from the CVS repository +2 - Committers/developers CVS access using SSH (to be written) +3 - Merging from DEVEL branch to main trunk (stable branch) +4 - How to release lwIP + + + +1 Obtaining lwIP from the CVS repository +---------------------------------------- + +To perform an anonymous CVS checkout of the main trunk (this is where +bug fixes and incremental enhancements occur), do this: + +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip + +Or, obtain a stable branch (updated with bug fixes only) as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7 -d lwip-0.7 lwip + +Or, obtain a specific (fixed) release as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7_0 -d lwip-0.7.0 lwip + +3 Committers/developers CVS access using SSH +-------------------------------------------- + +The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption. +As such, CVS commits to the server occur through a SSH tunnel for project members. +To create a SSH2 key pair in UNIX-like environments, do this: + +ssh-keygen -t dsa + +Under Windows, a recommended SSH client is "PuTTY", freely available with good +documentation and a graphic user interface. Use its key generator. + +Now paste the id_dsa.pub contents into your Savannah account public key list. Wait +a while so that Savannah can update its configuration (This can take minutes). + +Try to login using SSH: + +ssh -v your_login@cvs.sv.gnu.org + +If it tells you: + +Authenticating with public key "your_key_name"... +Server refused to allocate pty + +then you could login; Savannah refuses to give you a shell - which is OK, as we +are allowed to use SSH for CVS only. Now, you should be able to do this: + +export CVS_RSH=ssh +cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip + +after which you can edit your local files with bug fixes or new features and +commit them. Make sure you know what you are doing when using CVS to make +changes on the repository. If in doubt, ask on the lwip-members mailing list. + +(If SSH asks about authenticity of the host, you can check the key + fingerprint against http://savannah.nongnu.org/cvs/?group=lwip) + + +3 Merging from DEVEL branch to main trunk (stable) +-------------------------------------------------- + +Merging is a delicate process in CVS and requires the +following disciplined steps in order to prevent conflicts +in the future. Conflicts can be hard to solve! + +Merging from branch A to branch B requires that the A branch +has a tag indicating the previous merger. This tag is called +'merged_from_A_to_B'. After merging, the tag is moved in the +A branch to remember this merger for future merge actions. + +IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE +REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE +MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME). + +Merge all changes in DEVEL since our last merge to main: + +In the working copy of the main trunk: +cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL + +(This will apply the changes between 'merged_from_DEVEL_to_main' +and 'DEVEL' to your work set of files) + +We can now commit the merge result. +cvs commit -R -m "Merged from DEVEL to main." + +If this worked out OK, we now move the tag in the DEVEL branch +to this merge point, so we can use this point for future merges: + +cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip + +4 How to release lwIP +--------------------- + +First, checkout a clean copy of the branch to be released. Tag this set with +tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example). + +Login CVS using pserver authentication, then export a clean copy of the +tagged tree. Export is similar to a checkout, except that the CVS metadata +is not created locally. + +export CVS_RSH=ssh +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_6_3 -d lwip-0.6.3 lwip + +Archive this directory using tar, gzip'd, bzip2'd and zip'd. + +tar czvf lwip-0.6.3.tar.gz lwip-0.6.3 +tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3 +zip -r lwip-0.6.3.zip lwip-0.6.3 + +Now, sign the archives with a detached GPG binary signature as follows: + +gpg -b lwip-0.6.3.tar.gz +gpg -b lwip-0.6.3.tar.bz2 +gpg -b lwip-0.6.3.zip + +Upload these files using anonymous FTP: +ncftp ftp://savannah.gnu.org/incoming/savannah/lwip + +ncftp>mput *0.6.3.* + +Additionally, you may post a news item on Savannah, like this: + +A new 0.6.3 release is now available here: +http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3 + +You will have to submit this via the user News interface, then approve +this via the Administrator News interface. \ No newline at end of file diff --git a/packages/net/lwip_tcpip/current/doc/snmp_agent.txt b/packages/net/lwip_tcpip/current/doc/snmp_agent.txt new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/doc/snmp_agent.txt @@ -0,0 +1,181 @@ +SNMPv1 agent for lwIP + +Author: Christiaan Simons + +This is a brief introduction how to use and configure the SNMP agent. +Note the agent uses the raw-API UDP interface so you may also want to +read rawapi.txt to gain a better understanding of the SNMP message handling. + +0 Agent Capabilities +==================== + +SNMPv1 per RFC1157 + This is an old(er) standard but is still widely supported. + For SNMPv2c and v3 have a greater complexity and need many + more lines of code. IMHO this breaks the idea of "lightweight IP". + + Note the S in SNMP stands for "Simple". Note that "Simple" is + relative. SNMP is simple compared to the complex ISO network + management protocols CMIP (Common Management Information Protocol) + and CMOT (CMip Over Tcp). + +MIB II per RFC1213 + The standard lwIP stack management information base. + This is a required MIB, so this is always enabled. + When builing lwIP without TCP, the mib-2.tcp group is omitted. + The groups EGP, CMOT and transmission are disabled by default. + + Most mib-2 objects are not writable except: + sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + Writing to or changing the ARP and IP address and route + tables is not possible. + + Note lwIP has a very limited notion of IP routing. It currently + doen't have a route table and doesn't have a notion of the U,G,H flags. + Instead lwIP uses the interface list with only one default interface + acting as a single gateway interface (G) for the default route. + + The agent returns a "virtual table" with the default route 0.0.0.0 + for the default interface and network routes (no H) for each + network interface in the netif_list. + All routes are considered to be up (U). + +Loading additional MIBs + MIBs can only be added in compile-time, not in run-time. + There is no MIB compiler thus additional MIBs must be hand coded. + +Large SNMP message support + The packet decoding and encoding routines are designed + to use pbuf-chains. Larger payloads then the minimum + SNMP requirement of 484 octets are supported if the + PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your + local requirement. + +1 Building the Agent +==================== + +First of all you'll need to add the following define +to your local lwipopts.h: + +#define LWIP_SNMP 1 + +and add the source files in lwip/src/core/snmp +and some snmp headers in lwip/src/include/lwip to your makefile. + +Note you'll might need to adapt you network driver to update +the mib2 variables for your interface. + +2 Running the Agent +=================== + +The following function calls must be made in your program to +actually get the SNMP agent running. + +Before starting the agent you should supply pointers +to non-volatile memory for sysContact, sysLocation, +and snmpEnableAuthenTraps. You can do this by calling + +snmp_set_syscontact() +snmp_set_syslocation() +snmp_set_snmpenableauthentraps() + +Additionally you may want to set + +snmp_set_sysdescr() +snmp_set_sysobjid() (if you have a private MIB) +snmp_set_sysname() + +Also before starting the agent you need to setup +one or more trap destinations using these calls: + +snmp_trap_dst_enable(); +snmp_trap_dst_ip_set(); + +In the lwIP initialisation sequence call snmp_init() just after +the call to udp_init(). + +Exactly every 10 msec the SNMP uptime timestamp must be updated with +snmp_inc_sysuptime(). You should call this from a timer interrupt +or a timer signal handler depending on your runtime environment. + +An alternative way to update the SNMP uptime timestamp is to do a call like +snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to +a lower frequency). Another one is to not call snmp_inc_sysuptime() or +snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. +This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside +snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only +when it's queried (any function which need "sysuptime" have to call +snmp_get_sysuptime). + + +3 Private MIBs +============== + +If want to extend the agent with your own private MIB you'll need to +add the following define to your local lwipopts.h: + +#define SNMP_PRIVATE_MIB 1 + +You must provide the private_mib.h and associated files yourself. +Note we don't have a "MIB compiler" that generates C source from a MIB, +so you're required to do some serious coding if you enable this! + +Note the lwIP enterprise ID (26381) is assigned to the lwIP project, +ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP +MAINTAINERS! + +If you need to create your own private MIB you'll need +to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html + +You can set it by passing a struct snmp_obj_id to the agent +using snmp_set_sysobjid(&my_object_id), just before snmp_init(). + +Note the object identifiers for thes MIB-2 and your private MIB +tree must be kept in sorted ascending (lexicographical) order. +This to ensure correct getnext operation. + +An example for a private MIB is part of the "minimal Unix" project: +contrib/ports/unix/proj/minimal/lwip_prvmib.c + +The next chapter gives a more detailed description of the +MIB-2 tree and the optional private MIB. + +4 The Gory Details +================== + +4.0 Object identifiers and the MIB tree. + +We have three distinct parts for all object identifiers: + +The prefix + .iso.org.dod.internet + +the middle part + .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress + +and the index part + .1.192.168.0.1 + +Objects located above the .internet hierarchy aren't supported. +Currently only the .mgmt sub-tree is available and +when the SNMP_PRIVATE_MIB is enabled the .private tree +becomes available too. + +Object identifiers from incoming requests are checked +for a matching prefix, middle part and index part +or are expanded(*) for GetNext requests with short +or inexisting names in the request. +(* we call this "expansion" but this also +resembles the "auto-completion" operation) + +The middle part is usually located in ROM (const) +to preserve precious RAM on small microcontrollers. +However RAM location is possible for an dynamically +changing private tree. + +The index part is handled by functions which in +turn use dynamically allocated index trees from RAM. +These trees are updated by e.g. the etharp code +when new entries are made or removed form the ARP cache. + +/** @todo more gory details */ diff --git a/packages/net/lwip_tcpip/current/doc/sys_arch.txt b/packages/net/lwip_tcpip/current/doc/sys_arch.txt new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/doc/sys_arch.txt @@ -0,0 +1,228 @@ +sys_arch interface for lwIP 0.6++ + +Author: Adam Dunkels + +The operating system emulation layer provides a common interface +between the lwIP code and the underlying operating system kernel. The +general idea is that porting lwIP to new architectures requires only +small changes to a few header files and a new sys_arch +implementation. It is also possible to do a sys_arch implementation +that does not rely on any underlying operating system. + +The sys_arch provides semaphores and mailboxes to lwIP. For the full +lwIP functionality, multiple threads support can be implemented in the +sys_arch, but this is not required for the basic lwIP +functionality. Previous versions of lwIP required the sys_arch to +implement timer scheduling as well but as of lwIP 0.5 this is +implemented in a higher layer. + +In addition to the source file providing the functionality of sys_arch, +the OS emulation layer must provide several header files defining +macros used throughout lwip. The files required and the macros they +must define are listed below the sys_arch description. + +Semaphores can be either counting or binary - lwIP works with both +kinds. Mailboxes are used for message passing and can be implemented +either as a queue which allows multiple messages to be posted to a +mailbox, or as a rendez-vous point where only one message can be +posted at a time. lwIP works with both kinds, but the former type will +be more efficient. A message in a mailbox is just a pointer, nothing +more. + +Semaphores are represented by the type "sys_sem_t" which is typedef'd +in the sys_arch.h file. Mailboxes are equivalently represented by the +type "sys_mbox_t". lwIP does not place any restrictions on how +sys_sem_t or sys_mbox_t are represented internally. + +The following functions must be implemented by the sys_arch: + +- void sys_init(void) + + Is called to initialize the sys_arch layer. + +- sys_sem_t sys_sem_new(u8_t count) + + Creates and returns a new semaphore. The "count" argument specifies + the initial state of the semaphore. + +- void sys_sem_free(sys_sem_t sem) + + Deallocates a semaphore. + +- void sys_sem_signal(sys_sem_t sem) + + Signals a semaphore. + +- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) + + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). If the "timeout" argument is zero, the thread should be + blocked until the semaphore is signalled. + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. + +- sys_mbox_t sys_mbox_new(int size) + + Creates an empty mailbox for maximum "size" elements. Elements stored + in mailboxes are pointers. You have to define macros "_MBOX_SIZE" + in your lwipopts.h, or ignore this parameter in your implementation + and use a default size. + +- void sys_mbox_free(sys_mbox_t mbox) + + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. + +- void sys_mbox_post(sys_mbox_t mbox, void *msg) + + Posts the "msg" to the mailbox. This function have to block until + the "msg" is really posted. + +- err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg) + + Try to post the "msg" to the mailbox. Returns ERR_MEM if this one + is full, else, ERR_OK if the "msg" is posted. + +- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) + + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). If "timeout" is 0, the thread should + be blocked until a message arrives. The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. + +- u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg) + + This is similar to sys_arch_mbox_fetch, however if a message is not + present in the mailbox, it immediately returns with the code + SYS_MBOX_EMPTY. On success 0 is returned. + + To allow for efficient implementations, this can be defined as a + function-like macro in sys_arch.h instead of a normal function. For + example, a naive implementation could be: + #define sys_arch_mbox_tryfetch(mbox,msg) \ + sys_arch_mbox_fetch(mbox,msg,1) + although this would introduce unnecessary delays. + +- struct sys_timeouts *sys_arch_timeouts(void) + + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is repressented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single thread sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. + +If threads are supported by the underlying operating system and if +such functionality is needed in lwIP, the following function will have +to be implemented as well: + +- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) + + Starts a new thread named "name" with priority "prio" that will begin its + execution in the function "thread()". The "arg" argument will be passed as an + argument to the thread() function. The stack size to used for this thread is + the "stacksize" parameter. The id of the new thread is returned. Both the id + and the priority are system dependent. + +- sys_prot_t sys_arch_protect(void) + + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. + +- void sys_arch_unprotect(sys_prot_t pval) + + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. + +Note: + +Be carefull with using mem_malloc() in sys_arch. When malloc() refers to +mem_malloc() you can run into a circular function call problem. In mem.c +mem_init() tries to allcate a semaphore using mem_malloc, which of course +can't be performed when sys_arch uses mem_malloc. + +------------------------------------------------------------------------------- +Additional files required for the "OS support" emulation layer: +------------------------------------------------------------------------------- + +cc.h - Architecture environment, some compiler specific, some + environment specific (probably should move env stuff + to sys_arch.h.) + + Typedefs for the types used by lwip - + u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t + + Compiler hints for packing lwip's structures - + PACK_STRUCT_FIELD(x) + PACK_STRUCT_STRUCT + PACK_STRUCT_BEGIN + PACK_STRUCT_END + + Platform specific diagnostic output - + LWIP_PLATFORM_DIAG(x) - non-fatal, print a message. + LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution. + Portability defines for printf formatters: + U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F + + "lightweight" synchronization mechanisms - + SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable. + SYS_ARCH_PROTECT(x) - enter protection mode. + SYS_ARCH_UNPROTECT(x) - leave protection mode. + + If the compiler does not provide memset() this file must include a + definition of it, or include a file which defines it. + + This file must either include a system-local which defines + the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO + to make lwip/arch.h define the codes which are used throughout. + + +perf.h - Architecture specific performance measurement. + Measurement calls made throughout lwip, these can be defined to nothing. + PERF_START - start measuring something. + PERF_STOP(x) - stop measuring something, and record the result. + +sys_arch.h - Tied to sys_arch.c + + Arch dependent types for the following objects: + sys_sem_t, sys_mbox_t, sys_thread_t, + And, optionally: + sys_prot_t + + Defines to set vars of sys_mbox_t and sys_sem_t to NULL. + SYS_MBOX_NULL NULL + SYS_SEM_NULL NULL diff --git a/packages/net/lwip_tcpip/current/host/extractdump.py b/packages/net/lwip_tcpip/current/host/extractdump.py new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/host/extractdump.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +import sys +import re +import binascii + +def extract_pppdump(input, output): + + fi = open(input, "r") + fo = open(output, "wb") + p = re.compile("PPPDUMP:(.*)") + for line in fi.readlines(): + m = p.match(line) + if m == None: + continue + hex = m.group(1).strip() + data = binascii.unhexlify(hex) + fo.write(data) + fi.close() + fo.close() + +# command line parsing +if len(sys.argv) != 3: + print "Usage: extractdump.py [input] [output]" + sys.exit() + +extract_pppdump(sys.argv[1], sys.argv[2]) diff --git a/packages/net/lwip_tcpip/current/include/arch/cc.h b/packages/net/lwip_tcpip/current/include/arch/cc.h --- a/packages/net/lwip_tcpip/current/include/arch/cc.h +++ b/packages/net/lwip_tcpip/current/include/arch/cc.h @@ -1,99 +1,93 @@ //========================================================================== -// ####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation // -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later -// version. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. // -// eCos is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// You should have received a copy of the GNU General Public License -// along with eCos; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // -// As a special exception, if other files instantiate templates or use -// macros or inline functions from this file, or you compile this file -// and link it with other works to produce a work based on this file, -// this file does not by itself cause the resulting work to be covered by -// the GNU General Public License. However the source code for this file -// must still be made available in accordance with section (3) of the GNU -// General Public License v2. +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. // -// This exception does not invalidate any other reasons why a work based -// on this file might be covered by the GNU General Public License. -// ------------------------------------------- -// ####ECOSGPLCOPYRIGHTEND#### +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### //========================================================================== -/* - * 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 - * - */ -#ifndef __ARCH_CC_H__ -#define __ARCH_CC_H__ +#ifndef __LWIP_ARCH_CC_H__ +#define __LWIP_ARCH_CC_H__ #include #include -//while EFAULT should have no meaning in eCos since there are no address spaces -//it is defined here because set/getsockopt in lwIP use it. -#define EFAULT 14 +#include + +typedef int sys_prot_t; + +#define LWIP_ERR_T int -//found no better place for this prototype -int lwip_init(void); +// Define error codes used by lwIP but not by eCos +#define EFAULT 14 +#define ELOOP 40 +#define ETIME 62 +#define ENSRNOTFOUND 163 -//#define LWIP_PROVIDE_ERRNO -#include +// Platform byteorder #if (CYG_BYTEORDER == CYG_LSBFIRST) #define BYTE_ORDER LITTLE_ENDIAN #else #define BYTE_ORDER BIG_ENDIAN #endif +// Define generic types used in lwIP +#ifndef __U8_T__ +typedef unsigned char u8_t; +# define __U8_T__ +#endif -typedef unsigned char u8_t; -typedef signed char s8_t; -typedef unsigned short u16_t; -typedef signed short s16_t; -typedef unsigned long u32_t; -typedef signed long s32_t; -typedef unsigned long mem_ptr_t; +#ifndef __S8_T__ +typedef signed char s8_t; +# define __S8_T__ +#endif + +#ifndef __U16_T__ +typedef unsigned short u16_t; +# define __U16_T__ +#endif +#ifndef __S16_T__ +typedef signed short s16_t; +# define __S16_T__ +#endif + +#ifndef __U32_T__ +typedef unsigned long u32_t; +# define __U32_T__ +#endif + +#ifndef __S32_T__ +typedef signed long s32_t; +# define __S32_T__ +#endif + +typedef unsigned long mem_ptr_t; + +// Define (sn)printf formatters for these lwIP types #define U16_F "u" #define S16_F "d" #define X16_F "x" @@ -101,18 +95,17 @@ typedef unsigned long mem_ptr_t; #define S32_F "ld" #define X32_F "lx" -#define PACK_STRUCT_FIELD(x) x __attribute__((packed)) +// Compiler hints for packing structures +#define PACK_STRUCT_FIELD(x) x #define PACK_STRUCT_STRUCT __attribute__((packed)) #define PACK_STRUCT_BEGIN #define PACK_STRUCT_END +// Prototypes for printf() and abort() #include #include -#define LWIP_PLATFORM_DIAG(x) {diag_printf x;} -#define LWIP_PLATFORM_ASSERT(x) {CYG_FAIL(x);} +// Plaform specific diagnostic output +#define LWIP_PLATFORM_DIAG(x) do {diag_printf x;} while(0) +#define LWIP_PLATFORM_ASSERT(x) do {CYG_FAIL(x);} while(0) -#define SYS_ARCH_DECL_PROTECT(x) -#define SYS_ARCH_PROTECT(x) -#define SYS_ARCH_UNPROTECT(x) - -#endif /* __ARCH_CC_H__ */ +#endif // __LWIP_ARCH_CC_H__ diff --git a/packages/net/lwip_tcpip/current/include/arch/perf.h b/packages/net/lwip_tcpip/current/include/arch/perf.h --- a/packages/net/lwip_tcpip/current/include/arch/perf.h +++ b/packages/net/lwip_tcpip/current/include/arch/perf.h @@ -1,74 +1,39 @@ //========================================================================== -// ####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation // -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later -// version. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. // -// eCos is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// You should have received a copy of the GNU General Public License -// along with eCos; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // -// As a special exception, if other files instantiate templates or use -// macros or inline functions from this file, or you compile this file -// and link it with other works to produce a work based on this file, -// this file does not by itself cause the resulting work to be covered by -// the GNU General Public License. However the source code for this file -// must still be made available in accordance with section (3) of the GNU -// General Public License v2. +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. // -// This exception does not invalidate any other reasons why a work based -// on this file might be covered by the GNU General Public License. -// ------------------------------------------- -// ####ECOSGPLCOPYRIGHTEND#### +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### //========================================================================== -/* - * 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 - * - */ -#ifndef __PERF_H__ -#define __PERF_H__ +#ifndef __LWIP_PERF_H__ +#define __LWIP_PERF_H__ #define PERF_START /* null definition */ #define PERF_STOP(x) /* null definition */ -#endif /* __PERF_H__ */ +#endif // __LWIP_PERF_H__ diff --git a/packages/net/lwip_tcpip/current/include/arch/sys_arch.h b/packages/net/lwip_tcpip/current/include/arch/sys_arch.h --- a/packages/net/lwip_tcpip/current/include/arch/sys_arch.h +++ b/packages/net/lwip_tcpip/current/include/arch/sys_arch.h @@ -1,47 +1,51 @@ //========================================================================== -// ####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation // -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later -// version. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. // -// eCos is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// You should have received a copy of the GNU General Public License -// along with eCos; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // -// As a special exception, if other files instantiate templates or use -// macros or inline functions from this file, or you compile this file -// and link it with other works to produce a work based on this file, -// this file does not by itself cause the resulting work to be covered by -// the GNU General Public License. However the source code for this file -// must still be made available in accordance with section (3) of the GNU -// General Public License v2. +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. // -// This exception does not invalidate any other reasons why a work based -// on this file might be covered by the GNU General Public License. -// ------------------------------------------- -// ####ECOSGPLCOPYRIGHTEND#### +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### //========================================================================== -#ifndef __SYS_ECOS_H__ -#define __SYS_ECOS_H__ +#ifndef __LWIP_SYS_ARCH_H__ +#define __LWIP_SYS_ARCH_H__ #include -#define SYS_MBOX_NULL (sys_mbox_t)NULL -#define SYS_SEM_NULL (sys_sem_t)NULL +#include + +#if !NO_SYS -typedef cyg_sem_t* sys_sem_t; -typedef cyg_handle_t sys_mbox_t; -typedef cyg_handle_t sys_thread_t; -#endif /* __SYS_ECOS_H__ */ +typedef cyg_sem_t * sys_sem_t; +typedef cyg_handle_t sys_mbox_t; +typedef cyg_handle_t sys_thread_t; +#define SYS_SEM_NULL ((sys_sem_t) NULL) +#define SYS_MBOX_NULL ((sys_mbox_t) NULL) + +#endif // !NO_SYS + +#endif // __LWIP_SYS_ARCH_H__ diff --git a/packages/net/lwip_tcpip/current/include/lwip.h b/packages/net/lwip_tcpip/current/include/lwip.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip.h @@ -0,0 +1,93 @@ +#ifndef CYGONCE_LWIP_H +#define CYGONCE_LWIP_H +//============================================================================= +// +// lwip.h +// +// lwIP networking stack. +// +//============================================================================= +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//============================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Date: 2008-12-09 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +//============================================================================= + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include "lwip/sys.h" +#include "lwip/api.h" + +// Serial device id's +#define SIO_DEV_SLIPIF 0 +#define SIO_DEV_PPPOS 1 + +#ifdef CYGFUN_LWIP_MODE_SIMPLE + +externC void cyg_lwip_simple_init(void); +externC void cyg_lwip_simple_restart(void); +externC void cyg_lwip_simple_poll(void); + +#endif // CYGFUN_LWIP_MODE_SIMPLE + + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL + +externC void cyg_lwip_sequential_init(void); + +externC sys_thread_t cyg_lwip_thread_new(char *name, + void (* thread)(void *arg), void *arg, + void *stack, int stacksize, int prio); + +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL + + +#ifdef __cplusplus +} +#endif + +//----------------------------------------------------------------------------- +#endif // CYGONCE_LWIP_H diff --git a/packages/net/lwip_tcpip/current/include/lwip/api.h b/packages/net/lwip_tcpip/current/include/lwip/api.h --- a/packages/net/lwip_tcpip/current/include/lwip/api.h +++ b/packages/net/lwip_tcpip/current/include/lwip/api.h @@ -33,33 +33,50 @@ #define __LWIP_API_H__ #include "lwip/opt.h" -#include "lwip/pbuf.h" -#include "lwip/sys.h" -#include "lwip/ip.h" +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ -#include "lwip/raw.h" -#include "lwip/udp.h" -#include "lwip/tcp.h" +#include /* for size_t */ +#include "lwip/netbuf.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" #include "lwip/err.h" -#define NETCONN_NOCOPY 0x00 +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ + +/* Flags for netconn_write */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ #define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) (t&0xF0) +#define NETCONNTYPE_DATAGRAM(t) (t&0xE0) enum netconn_type { - NETCONN_TCP, - NETCONN_UDP, - NETCONN_UDPLITE, - NETCONN_UDPNOCHKSUM, - NETCONN_RAW + NETCONN_INVALID = 0, + /* NETCONN_TCP Group */ + NETCONN_TCP = 0x10, + /* NETCONN_UDP Group */ + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM= 0x22, + /* NETCONN_RAW Group */ + NETCONN_RAW = 0x40 }; enum netconn_state { NETCONN_NONE, NETCONN_WRITE, - NETCONN_ACCEPT, - NETCONN_RECV, + NETCONN_LISTEN, NETCONN_CONNECT, NETCONN_CLOSE }; @@ -71,89 +88,135 @@ enum netconn_evt { NETCONN_EVT_SENDMINUS }; -struct netbuf { - struct pbuf *p, *ptr; - struct ip_addr *fromaddr; - u16_t fromport; - err_t err; +#if LWIP_IGMP +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE }; +#endif /* LWIP_IGMP */ +/* forward-declare some structs to avoid to include their headers */ +struct ip_pcb; +struct tcp_pcb; +struct udp_pcb; +struct raw_pcb; +struct netconn; + +/** A callback prototype to inform about events for a netconn */ +typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len); + +/** A netconn descriptor */ struct netconn { + /** type of the netconn (TCP, UDP or RAW) */ enum netconn_type type; + /** current state of the netconn */ enum netconn_state state; + /** the lwIP internal protocol control block */ union { + struct ip_pcb *ip; struct tcp_pcb *tcp; struct udp_pcb *udp; struct raw_pcb *raw; } pcb; + /** the last error this netconn had */ err_t err; - sys_mbox_t mbox; + /** sem that is used to synchroneously execute functions in the core context */ + sys_sem_t op_completed; + /** mbox where received packets are stored until they are fetched + by the netconn application thread (can grow quite big) */ sys_mbox_t recvmbox; + /** mbox where new connections are stored until processed + by the application thread */ sys_mbox_t acceptmbox; - sys_sem_t sem; + /** only used for socket layer */ int socket; - u16_t recv_avail; - void (* callback)(struct netconn *, enum netconn_evt, u16_t len); +#if LWIP_SO_RCVTIMEO + /** timeout to wait for new data to be received + (or connections to arrive for listening netconns) */ + int recv_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + /** maximum amount of bytes queued in recvmbox */ + int recv_bufsize; +#endif /* LWIP_SO_RCVBUF */ + s16_t recv_avail; +#if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. */ + struct api_msg_msg *write_msg; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores how much is already sent. */ + size_t write_offset; +#if LWIP_TCPIP_CORE_LOCKING + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores whether to wake up the original application task + if data couldn't be sent in the first try. */ + u8_t write_delayed; +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_TCP */ + /** A callback function that is informed about events for this netconn */ + netconn_callback callback; }; -/* Network buffer functions: */ -struct netbuf * netbuf_new (void); -void netbuf_delete (struct netbuf *buf); -void * netbuf_alloc (struct netbuf *buf, u16_t size); -void netbuf_free (struct netbuf *buf); -void netbuf_ref (struct netbuf *buf, - void *dataptr, u16_t size); -void netbuf_chain (struct netbuf *head, - struct netbuf *tail); - -u16_t netbuf_len (struct netbuf *buf); -err_t netbuf_data (struct netbuf *buf, - void **dataptr, u16_t *len); -s8_t netbuf_next (struct netbuf *buf); -void netbuf_first (struct netbuf *buf); - -void netbuf_copy (struct netbuf *buf, - void *dataptr, u16_t len); -void netbuf_copy_partial(struct netbuf *buf, void *dataptr, - u16_t len, u16_t offset); -struct ip_addr * netbuf_fromaddr (struct netbuf *buf); -u16_t netbuf_fromport (struct netbuf *buf); +/* Register an Network connection event */ +#define API_EVENT(c,e,l) if (c->callback) { \ + (*c->callback)(c, e, l); \ + } /* Network connection functions: */ -struct netconn * netconn_new (enum netconn_type type); +#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL) +#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c) struct -netconn *netconn_new_with_callback(enum netconn_type t, - void (*callback)(struct netconn *, enum netconn_evt, u16_t len)); -struct -netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u16_t proto, - void (*callback)(struct netconn *, enum netconn_evt, u16_t len)); +netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); err_t netconn_delete (struct netconn *conn); -enum netconn_type netconn_type (struct netconn *conn); -err_t netconn_peer (struct netconn *conn, - struct ip_addr *addr, - u16_t *port); -err_t netconn_addr (struct netconn *conn, - struct ip_addr **addr, - u16_t *port); +/** Get the type of a netconn (as enum netconn_type). */ +#define netconn_type(conn) (conn->type) + +err_t netconn_getaddr (struct netconn *conn, + struct ip_addr *addr, + u16_t *port, + u8_t local); +#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0) +#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1) + err_t netconn_bind (struct netconn *conn, - struct ip_addr *addr, - u16_t port); + struct ip_addr *addr, + u16_t port); err_t netconn_connect (struct netconn *conn, - struct ip_addr *addr, - u16_t port); + struct ip_addr *addr, + u16_t port); err_t netconn_disconnect (struct netconn *conn); -err_t netconn_listen (struct netconn *conn); +err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog); +#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG) struct netconn * netconn_accept (struct netconn *conn); struct netbuf * netconn_recv (struct netconn *conn); +err_t netconn_sendto (struct netconn *conn, + struct netbuf *buf, struct ip_addr *addr, u16_t port); err_t netconn_send (struct netconn *conn, - struct netbuf *buf); + struct netbuf *buf); err_t netconn_write (struct netconn *conn, - void *dataptr, u16_t size, - u8_t copy); + const void *dataptr, size_t size, + u8_t apiflags); err_t netconn_close (struct netconn *conn); -err_t netconn_err (struct netconn *conn); +#if LWIP_IGMP +err_t netconn_join_leave_group (struct netconn *conn, + struct ip_addr *multiaddr, + struct ip_addr *interface, + enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, struct ip_addr *addr); +#endif /* LWIP_DNS */ + +#define netconn_err(conn) ((conn)->err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ #endif /* __LWIP_API_H__ */ - - diff --git a/packages/net/lwip_tcpip/current/include/lwip/api_msg.h b/packages/net/lwip_tcpip/current/include/lwip/api_msg.h --- a/packages/net/lwip_tcpip/current/include/lwip/api_msg.h +++ b/packages/net/lwip_tcpip/current/include/lwip/api_msg.h @@ -33,62 +33,130 @@ #define __LWIP_API_MSG_H__ #include "lwip/opt.h" -#include "lwip/pbuf.h" -#include "lwip/sys.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ -#include "lwip/ip.h" - -#include "lwip/udp.h" -#include "lwip/tcp.h" - +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" #include "lwip/api.h" -enum api_msg_type { - API_MSG_NEWCONN, - API_MSG_DELCONN, - - API_MSG_BIND, - API_MSG_CONNECT, - API_MSG_DISCONNECT, - - API_MSG_LISTEN, - API_MSG_ACCEPT, +#ifdef __cplusplus +extern "C" { +#endif - API_MSG_SEND, - API_MSG_RECV, - API_MSG_WRITE, - - API_MSG_CLOSE, - - API_MSG_MAX -}; - +/* IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ +/** This struct includes everything that is necessary to execute a function + for a netconn in another thread context (mainly used to process netconns + in the tcpip_thread context to be thread safe). */ struct api_msg_msg { + /** The netconn which to process - always needed: it includes the semaphore + which is used to block the application thread until the function finished. */ struct netconn *conn; - enum netconn_type conntype; + /** Depending on the executed function, one of these union members is used */ union { - struct pbuf *p; - struct { + /** used for do_send */ + struct netbuf *b; + /** used for do_newconn */ + struct { + u8_t proto; + } n; + /** used for do_bind and do_connect */ + struct { struct ip_addr *ipaddr; u16_t port; } bc; + /** used for do_getaddr */ struct { - void *dataptr; + struct ip_addr *ipaddr; + u16_t *port; + u8_t local; + } ad; + /** used for do_write */ + struct { + const void *dataptr; + size_t len; + u8_t apiflags; + } w; + /** used for do_recv */ + struct { u16_t len; - u8_t copy; - } w; - sys_mbox_t mbox; - u16_t len; + } r; +#if LWIP_IGMP + /** used for do_join_leave_group */ + struct { + struct ip_addr *multiaddr; + struct ip_addr *interface; + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ } msg; }; +/** This struct contains a function to execute in another thread context and + a struct api_msg_msg that serves as an argument for this function. + This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */ struct api_msg { - enum api_msg_type type; + /** function to execute in tcpip_thread context */ + void (* function)(struct api_msg_msg *msg); + /** arguments for this function */ struct api_msg_msg msg; }; -void api_msg_input(struct api_msg *msg); -void api_msg_post(struct api_msg *msg); +#if LWIP_DNS +/** As do_gethostbyname requires more arguments but doesn't require a netconn, + it has its own struct (to avoid struct api_msg getting bigger than necessary). + do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + (see netconn_gethostbyname). */ +struct dns_api_msg { + /** Hostname to query or dotted IP address string */ + const char *name; + /** Rhe resolved address is stored here */ + struct ip_addr *addr; + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t sem; + /** Errors are given back here */ + err_t *err; +}; +#endif /* LWIP_DNS */ + +void do_newconn ( struct api_msg_msg *msg); +void do_delconn ( struct api_msg_msg *msg); +void do_bind ( struct api_msg_msg *msg); +void do_connect ( struct api_msg_msg *msg); +void do_disconnect ( struct api_msg_msg *msg); +void do_listen ( struct api_msg_msg *msg); +void do_send ( struct api_msg_msg *msg); +void do_recv ( struct api_msg_msg *msg); +void do_write ( struct api_msg_msg *msg); +void do_getaddr ( struct api_msg_msg *msg); +void do_close ( struct api_msg_msg *msg); +#if LWIP_IGMP +void do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +void do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ #endif /* __LWIP_API_MSG_H__ */ - diff --git a/packages/net/lwip_tcpip/current/include/lwip/arch.h b/packages/net/lwip_tcpip/current/include/lwip/arch.h --- a/packages/net/lwip_tcpip/current/include/lwip/arch.h +++ b/packages/net/lwip_tcpip/current/include/lwip/arch.h @@ -42,6 +42,15 @@ #include "arch/cc.h" +/** Temporary: define format string for size_t if not defined in cc.h */ +#ifndef SZT_F +#define SZT_F U32_F +#endif /* SZT_F */ + +#ifdef __cplusplus +extern "C" { +#endif + #ifndef PACK_STRUCT_BEGIN #define PACK_STRUCT_BEGIN #endif /* PACK_STRUCT_BEGIN */ @@ -55,6 +64,10 @@ #endif /* PACK_STRUCT_FIELD */ +#ifndef LWIP_UNUSED_ARG +#define LWIP_UNUSED_ARG(x) (void)x +#endif /* LWIP_UNUSED_ARG */ + #ifdef LWIP_PROVIDE_ERRNO @@ -187,25 +200,25 @@ #define EMEDIUMTYPE 124 /* Wrong medium type */ -#define ENSROK 0 /* DNS server returned answer with no data */ -#define ENSRNODATA 160 /* DNS server returned answer with no data */ -#define ENSRFORMERR 161 /* DNS server claims query was misformatted */ -#define ENSRSERVFAIL 162 /* DNS server returned general failure */ -#define ENSRNOTFOUND 163 /* Domain name not found */ -#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */ -#define ENSRREFUSED 165 /* DNS server refused query */ -#define ENSRBADQUERY 166 /* Misformatted DNS query */ -#define ENSRBADNAME 167 /* Misformatted domain name */ -#define ENSRBADFAMILY 168 /* Unsupported address family */ -#define ENSRBADRESP 169 /* Misformatted DNS reply */ -#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */ -#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */ -#define ENSROF 172 /* End of file */ -#define ENSRFILE 173 /* Error reading file */ -#define ENSRNOMEM 174 /* Out of memory */ -#define ENSRDESTRUCTION 175 /* Application terminated lookup */ -#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */ -#define ENSRCNAMELOOP 177 /* Domain name is too long */ +#define ENSROK 0 /* DNS server returned answer with no data */ +#define ENSRNODATA 160 /* DNS server returned answer with no data */ +#define ENSRFORMERR 161 /* DNS server claims query was misformatted */ +#define ENSRSERVFAIL 162 /* DNS server returned general failure */ +#define ENSRNOTFOUND 163 /* Domain name not found */ +#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */ +#define ENSRREFUSED 165 /* DNS server refused query */ +#define ENSRBADQUERY 166 /* Misformatted DNS query */ +#define ENSRBADNAME 167 /* Misformatted domain name */ +#define ENSRBADFAMILY 168 /* Unsupported address family */ +#define ENSRBADRESP 169 /* Misformatted DNS reply */ +#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */ +#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */ +#define ENSROF 172 /* End of file */ +#define ENSRFILE 173 /* Error reading file */ +#define ENSRNOMEM 174 /* Out of memory */ +#define ENSRDESTRUCTION 175 /* Application terminated lookup */ +#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */ +#define ENSRCNAMELOOP 177 /* Domain name is too long */ #ifndef errno extern int errno; @@ -213,4 +226,8 @@ extern int errno; #endif /* LWIP_PROVIDE_ERRNO */ +#ifdef __cplusplus +} +#endif + #endif /* __LWIP_ARCH_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/autoip.h b/packages/net/lwip_tcpip/current/include/lwip/autoip.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/autoip.h @@ -0,0 +1,116 @@ +/** + * @file + * + * AutoIP Automatic LinkLocal IP Configuration + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +#ifndef __LWIP_AUTOIP_H__ +#define __LWIP_AUTOIP_H__ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* AutoIP Timing */ +#define AUTOIP_TMR_INTERVAL 100 +#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL) + +/* RFC 3927 Constants */ +#define PROBE_WAIT 1 /* second (initial random delay) */ +#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */ +#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */ +#define PROBE_NUM 3 /* (number of probe packets) */ +#define ANNOUNCE_NUM 2 /* (number of announcement packets) */ +#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ +#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ + +/* AutoIP client states */ +#define AUTOIP_STATE_OFF 0 +#define AUTOIP_STATE_PROBING 1 +#define AUTOIP_STATE_ANNOUNCING 2 +#define AUTOIP_STATE_BOUND 3 + +struct autoip +{ + struct ip_addr llipaddr; /* the currently selected, probed, announced or used LL IP-Address */ + u8_t state; /* current AutoIP state machine state */ + u8_t sent_num; /* sent number of probes or announces, dependent on state */ + u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */ + u8_t lastconflict; /* ticks until a conflict can be solved by defending */ + u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */ +}; + + +/** Init srand, has to be called before entering mainloop */ +void autoip_init(void); + +/** Start AutoIP client */ +err_t autoip_start(struct netif *netif); + +/** Stop AutoIP client */ +err_t autoip_stop(struct netif *netif); + +/** Handles every incoming ARP Packet, called by etharp_arp_input */ +void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr); + +/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */ +void autoip_tmr(void); + +/** Handle a possible change in the network configuration */ +void autoip_network_changed(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_AUTOIP */ + +#endif /* __LWIP_AUTOIP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/debug.h b/packages/net/lwip_tcpip/current/include/lwip/debug.h --- a/packages/net/lwip_tcpip/current/include/lwip/debug.h +++ b/packages/net/lwip_tcpip/current/include/lwip/debug.h @@ -32,56 +32,67 @@ #ifndef __LWIP_DEBUG_H__ #define __LWIP_DEBUG_H__ -#include "arch/cc.h" +#include "lwip/arch.h" /** lower two bits indicate debug level - * - 0 off + * - 0 all * - 1 warning * - 2 serious * - 3 severe */ - -#define DBG_LEVEL_OFF 0 -#define DBG_LEVEL_WARNING 1 /* bad checksums, dropped packets, ... */ -#define DBG_LEVEL_SERIOUS 2 /* memory allocation failures, ... */ -#define DBG_LEVEL_SEVERE 3 /* */ -#define DBG_MASK_LEVEL 3 +#define LWIP_DBG_LEVEL_ALL 0x00 +#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */ +#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */ +#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */ +#define LWIP_DBG_LEVEL_SEVERE 0x03 +#define LWIP_DBG_MASK_LEVEL 0x03 /** flag for LWIP_DEBUGF to enable that debug message */ -#define DBG_ON 0x80U +#define LWIP_DBG_ON 0x80U /** flag for LWIP_DEBUGF to disable that debug message */ -#define DBG_OFF 0x00U +#define LWIP_DBG_OFF 0x00U /** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */ -#define DBG_TRACE 0x40U +#define LWIP_DBG_TRACE 0x40U /** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */ -#define DBG_STATE 0x20U +#define LWIP_DBG_STATE 0x20U /** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */ -#define DBG_FRESH 0x10U +#define LWIP_DBG_FRESH 0x10U /** flag for LWIP_DEBUGF to halt after printing this debug message */ -#define DBG_HALT 0x08U +#define LWIP_DBG_HALT 0x08U #ifndef LWIP_NOASSERT -# define LWIP_ASSERT(x,y) do { if(!(y)) LWIP_PLATFORM_ASSERT(x); } while(0) -#else -# define LWIP_ASSERT(x,y) -#endif +#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \ + LWIP_PLATFORM_ASSERT(message); } while(0) +#else /* LWIP_NOASSERT */ +#define LWIP_ASSERT(message, assertion) +#endif /* LWIP_NOASSERT */ + +/** if "expression" isn't true, then print "message" and execute "handler" expression */ +#ifndef LWIP_ERROR +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + LWIP_PLATFORM_ASSERT(message); handler;}} while(0) +#endif /* LWIP_ERROR */ #ifdef LWIP_DEBUG /** print debug message only if debug message type is enabled... - * AND is of correct type AND is at least DBG_LEVEL + * AND is of correct type AND is at least LWIP_DBG_LEVEL */ -# define LWIP_DEBUGF(debug,x) do { if (((debug) & DBG_ON) && ((debug) & DBG_TYPES_ON) && ((s16_t)((debug) & DBG_MASK_LEVEL) >= DBG_MIN_LEVEL)) { LWIP_PLATFORM_DIAG(x); if ((debug) & DBG_HALT) while(1); } } while(0) -# define LWIP_ERROR(x) do { LWIP_PLATFORM_DIAG(x); } while(0) -#else /* LWIP_DEBUG */ -# define LWIP_DEBUGF(debug,x) -# define LWIP_ERROR(x) +#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ + LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ + } \ + } while(0) + +#else /* LWIP_DEBUG */ +#define LWIP_DEBUGF(debug, message) #endif /* LWIP_DEBUG */ #endif /* __LWIP_DEBUG_H__ */ - - - - - diff --git a/packages/net/lwip_tcpip/current/include/lwip/def.h b/packages/net/lwip_tcpip/current/include/lwip/def.h --- a/packages/net/lwip_tcpip/current/include/lwip/def.h +++ b/packages/net/lwip_tcpip/current/include/lwip/def.h @@ -33,10 +33,10 @@ #define __LWIP_DEF_H__ /* this might define NULL already */ -#include "arch/cc.h" +#include "lwip/arch.h" -#define LWIP_MAX(x , y) (x) > (y) ? (x) : (y) -#define LWIP_MIN(x , y) (x) < (y) ? (x) : (y) +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) #ifndef NULL #define NULL ((void *)0) diff --git a/packages/net/lwip_tcpip/current/include/lwip/dhcp.h b/packages/net/lwip_tcpip/current/include/lwip/dhcp.h --- a/packages/net/lwip_tcpip/current/include/lwip/dhcp.h +++ b/packages/net/lwip_tcpip/current/include/lwip/dhcp.h @@ -5,32 +5,39 @@ #define __LWIP_DHCP_H__ #include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + #include "lwip/netif.h" #include "lwip/udp.h" +#ifdef __cplusplus +extern "C" { +#endif + /** period (in seconds) of the application calling dhcp_coarse_tmr() */ #define DHCP_COARSE_TIMER_SECS 60 +/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL) /** period (in milliseconds) of the application calling dhcp_fine_tmr() */ #define DHCP_FINE_TIMER_MSECS 500 struct dhcp { - /** current DHCP state machine state */ - u8_t state; - /** retries of current request */ - u8_t tries; /** transaction identifier of last sent request */ u32_t xid; /** our connection to the DHCP server */ struct udp_pcb *pcb; - /** (first) pbuf of incoming msg */ - struct pbuf *p; /** incoming msg */ struct dhcp_msg *msg_in; /** incoming msg options */ - struct dhcp_msg *options_in; + void *options_in; /** ingoing msg options length */ u16_t options_in_len; + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; struct pbuf *p_out; /* pbuf of outcoming msg */ struct dhcp_msg *msg_out; /* outgoing msg */ @@ -50,6 +57,9 @@ struct dhcp u32_t offered_t0_lease; /* lease period (in seconds) */ u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */ +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif /** Patch #1308 * TODO: See dhcp.c "TODO"s */ @@ -112,6 +122,8 @@ err_t dhcp_release(struct netif *netif); void dhcp_stop(struct netif *netif); /** inform server of our manual IP address */ void dhcp_inform(struct netif *netif); +/** Handle a possible change in the network configuration */ +void dhcp_network_changed(struct netif *netif); /** if enabled, check whether the offered IP address is not in use, using ARP */ #if DHCP_DOES_ARP_CHECK @@ -161,6 +173,10 @@ void dhcp_fine_tmr(void); /** not yet implemented #define DHCP_RELEASING 11 */ #define DHCP_BACKING_OFF 12 #define DHCP_OFF 13 + +/** AUTOIP cooperatation flags */ +#define DHCP_AUTOIP_COOP_STATE_OFF 0 +#define DHCP_AUTOIP_COOP_STATE_ON 1 #define DHCP_BOOTREQUEST 1 #define DHCP_BOOTREPLY 2 @@ -210,6 +226,7 @@ void dhcp_fine_tmr(void); #define DHCP_OPTION_T1 58 /* T1 renewal time */ #define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 #define DHCP_OPTION_CLIENT_ID 61 #define DHCP_OPTION_TFTP_SERVERNAME 66 #define DHCP_OPTION_BOOTFILE 67 @@ -220,4 +237,10 @@ void dhcp_fine_tmr(void); #define DHCP_OVERLOAD_SNAME 2 #define DHCP_OVERLOAD_SNAME_FILE 3 +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DHCP */ + #endif /*__LWIP_DHCP_H__*/ diff --git a/packages/net/lwip_tcpip/current/include/lwip/dns.h b/packages/net/lwip_tcpip/current/include/lwip/dns.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/dns.h @@ -0,0 +1,97 @@ +/** + * lwip DNS resolver header file. + + * Author: Jim Pettinato + * April 2007 + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __LWIP_DNS_H__ +#define __LWIP_DNS_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/** DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ + +/** DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to a struct ip_addr containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +typedef void (*dns_found_callback)(const char *name, struct ip_addr *ipaddr, void *callback_arg); + + +void dns_init(void); + +void dns_tmr(void); + +void dns_setserver(u8_t numdns, struct ip_addr *dnsserver); + +struct ip_addr dns_getserver(u8_t numdns); + +err_t dns_gethostbyname(const char *hostname, struct ip_addr *addr, + dns_found_callback found, void *callback_arg); + +#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +int dns_local_removehost(const char *hostname, const struct ip_addr *addr); +err_t dns_local_addhost(const char *hostname, const struct ip_addr *addr); +#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/err.h b/packages/net/lwip_tcpip/current/include/lwip/err.h --- a/packages/net/lwip_tcpip/current/include/lwip/err.h +++ b/packages/net/lwip_tcpip/current/include/lwip/err.h @@ -33,38 +33,55 @@ #define __LWIP_ERR_H__ #include "lwip/opt.h" +#include "lwip/arch.h" -#include "arch/cc.h" +#ifdef __cplusplus +extern "C" { +#endif -typedef s8_t err_t; +/** Define LWIP_ERR_T in cc.h if you want to use + * a different type for your platform (must be signed). */ +#ifdef LWIP_ERR_T +typedef LWIP_ERR_T err_t; +#else /* LWIP_ERR_T */ + typedef s8_t err_t; +#endif /* LWIP_ERR_T*/ /* Definitions for error constants. */ -#define ERR_OK 0 /* No error, everything OK. */ -#define ERR_MEM -1 /* Out of memory error. */ -#define ERR_BUF -2 /* Buffer error. */ - +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ +#define ERR_RTE -4 /* Routing problem. */ -#define ERR_ABRT -3 /* Connection aborted. */ -#define ERR_RST -4 /* Connection reset. */ -#define ERR_CLSD -5 /* Connection closed. */ -#define ERR_CONN -6 /* Not connected. */ +#define ERR_IS_FATAL(e) ((e) < ERR_RTE) -#define ERR_VAL -7 /* Illegal value. */ +#define ERR_ABRT -5 /* Connection aborted. */ +#define ERR_RST -6 /* Connection reset. */ +#define ERR_CLSD -7 /* Connection closed. */ +#define ERR_CONN -8 /* Not connected. */ -#define ERR_ARG -8 /* Illegal argument. */ +#define ERR_VAL -9 /* Illegal value. */ -#define ERR_RTE -9 /* Routing problem. */ +#define ERR_ARG -10 /* Illegal argument. */ + +#define ERR_USE -11 /* Address in use. */ -#define ERR_USE -10 /* Address in use. */ +#define ERR_IF -12 /* Low-level netif error */ +#define ERR_ISCONN -13 /* Already connected. */ -#define ERR_IF -11 /* Low-level netif error */ -#define ERR_ISCONN -12 /* Already connected. */ +#define ERR_INPROGRESS -14 /* Operation in progress */ #ifdef LWIP_DEBUG -extern char *lwip_strerr(err_t err); +extern const char *lwip_strerr(err_t err); #else #define lwip_strerr(x) "" #endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + #endif /* __LWIP_ERR_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/icmp.h b/packages/net/lwip_tcpip/current/include/lwip/icmp.h --- a/packages/net/lwip_tcpip/current/include/lwip/icmp.h +++ b/packages/net/lwip_tcpip/current/include/lwip/icmp.h @@ -32,14 +32,15 @@ #ifndef __LWIP_ICMP_H__ #define __LWIP_ICMP_H__ -#include "lwip/arch.h" - #include "lwip/opt.h" #include "lwip/pbuf.h" - #include "lwip/ip_addr.h" #include "lwip/netif.h" +#ifdef __cplusplus +extern "C" { +#endif + #define ICMP_ER 0 /* echo reply */ #define ICMP_DUR 3 /* destination unreachable */ #define ICMP_SQ 4 /* source quench */ @@ -66,47 +67,45 @@ enum icmp_te_type { ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ }; -void icmp_input(struct pbuf *p, struct netif *inp); - -void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); -void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); - #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif +/** This is the standard ICMP header only that the u32_t data + * is splitted to two u16_t like ICMP echo needs it. + * This header is also used for other ICMP types that do not + * use the data part. + */ PACK_STRUCT_BEGIN struct icmp_echo_hdr { - PACK_STRUCT_FIELD(u16_t _type_code); + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); PACK_STRUCT_FIELD(u16_t chksum); PACK_STRUCT_FIELD(u16_t id); PACK_STRUCT_FIELD(u16_t seqno); } PACK_STRUCT_STRUCT; PACK_STRUCT_END - -PACK_STRUCT_BEGIN -struct icmp_dur_hdr { - PACK_STRUCT_FIELD(u16_t _type_code); - PACK_STRUCT_FIELD(u16_t chksum); - PACK_STRUCT_FIELD(u32_t unused); -} PACK_STRUCT_STRUCT; -PACK_STRUCT_END - -PACK_STRUCT_BEGIN -struct icmp_te_hdr { - PACK_STRUCT_FIELD(u16_t _type_code); - PACK_STRUCT_FIELD(u16_t chksum); - PACK_STRUCT_FIELD(u32_t unused); -} PACK_STRUCT_STRUCT; -PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/epstruct.h" #endif -#define ICMPH_TYPE(hdr) (ntohs((hdr)->_type_code) >> 8) -#define ICMPH_CODE(hdr) (ntohs((hdr)->_type_code) & 0xff) +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t */ +#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t)) +#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c)) + + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ -#define ICMPH_TYPE_SET(hdr, type) ((hdr)->_type_code = htons(ICMPH_CODE(hdr) | ((type) << 8))) -#define ICMPH_CODE_SET(hdr, code) ((hdr)->_type_code = htons((code) | (ICMPH_TYPE(hdr) << 8))) +void icmp_input(struct pbuf *p, struct netif *inp); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_ICMP */ + +#ifdef __cplusplus +} +#endif #endif /* __LWIP_ICMP_H__ */ - diff --git a/packages/net/lwip_tcpip/current/include/lwip/igmp.h b/packages/net/lwip_tcpip/current/include/lwip/igmp.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/igmp.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * 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 CITEL Technologies Ltd 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 CITEL TECHNOLOGIES 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 CITEL TECHNOLOGIES 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 a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +#ifndef __LWIP_IGMP_H__ +#define __LWIP_IGMP_H__ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * IGMP constants + */ +#define IP_PROTO_IGMP 2 +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404 +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* IGMP timer */ +#define IGMP_TMR_INTERVAL 100 /* Milliseconds */ +#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL) + +/* MAC Filter Actions */ +#define IGMP_DEL_MAC_FILTER 0 +#define IGMP_ADD_MAC_FILTER 1 + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/* + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FIELD(u8_t igmp_msgtype); + PACK_STRUCT_FIELD(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FIELD(struct ip_addr igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * now a group structure - there is + * a list of groups for each interface + * these should really be linked from the interface, but + * if we keep them separate we will not affect the lwip original code + * too much + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ + +struct igmp_group { + struct igmp_group *next; + struct netif *interface; + struct ip_addr group_address; + u8_t last_reporter_flag; /* signifies we were the last person to report */ + u8_t group_state; + u16_t timer; + u8_t use; /* counter of simultaneous uses */ +}; + + +/* Prototypes */ +void igmp_init(void); + +err_t igmp_start( struct netif *netif); + +err_t igmp_stop( struct netif *netif); + +void igmp_report_groups( struct netif *netif); + +struct igmp_group *igmp_lookfor_group( struct netif *ifp, struct ip_addr *addr); + +struct igmp_group *igmp_lookup_group( struct netif *ifp, struct ip_addr *addr); + +err_t igmp_remove_group( struct igmp_group *group); + +void igmp_input( struct pbuf *p, struct netif *inp, struct ip_addr *dest); + +err_t igmp_joingroup( struct ip_addr *ifaddr, struct ip_addr *groupaddr); + +err_t igmp_leavegroup( struct ip_addr *ifaddr, struct ip_addr *groupaddr); + +void igmp_tmr(void); + +void igmp_timeout( struct igmp_group *group); + +void igmp_start_timer( struct igmp_group *group, u8_t max_time); + +void igmp_stop_timer( struct igmp_group *group); + +void igmp_delaying_member( struct igmp_group *group, u8_t maxresp); + +err_t igmp_ip_output_if( struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t proto, struct netif *netif); + +void igmp_send( struct igmp_group *group, u8_t type); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/inet.h b/packages/net/lwip_tcpip/current/include/lwip/inet.h --- a/packages/net/lwip_tcpip/current/include/lwip/inet.h +++ b/packages/net/lwip_tcpip/current/include/lwip/inet.h @@ -32,24 +32,25 @@ #ifndef __LWIP_INET_H__ #define __LWIP_INET_H__ -#include "lwip/arch.h" +#include "lwip/opt.h" -#include "lwip/opt.h" -#include "lwip/pbuf.h" -#include "lwip/ip_addr.h" +#ifdef __cplusplus +extern "C" { +#endif -u16_t inet_chksum(void *dataptr, u16_t len); -#if 0 /* optimized routine */ -u16_t inet_chksum4(u8_t *dataptr, u16_t len); -#endif -u16_t inet_chksum_pbuf(struct pbuf *p); -u16_t inet_chksum_pseudo(struct pbuf *p, - struct ip_addr *src, struct ip_addr *dest, - u8_t proto, u16_t proto_len); +/* For compatibility with BSD code */ +struct in_addr { + u32_t s_addr; +}; -externC u32_t inet_addr(const char *cp); -externC s8_t inet_aton(const char *cp, struct in_addr *addr); -externC char *inet_ntoa(struct in_addr addr); /* returns ptr to static buffer; not reentrant! */ +#define INADDR_NONE ((u32_t)0xffffffffUL) /* 255.255.255.255 */ +#define INADDR_LOOPBACK ((u32_t)0x7f000001UL) /* 127.0.0.1 */ +#define INADDR_ANY ((u32_t)0x00000000UL) /* 0.0.0.0 */ +#define INADDR_BROADCAST ((u32_t)0xffffffffUL) /* 255.255.255.255 */ + +u32_t inet_addr(const char *cp); +int inet_aton(const char *cp, struct in_addr *addr); +char *inet_ntoa(struct in_addr addr); /* returns ptr to static buffer; not reentrant! */ #ifdef htons #undef htons @@ -64,24 +65,39 @@ externC char *inet_ntoa(struct in_addr a #undef ntohl #endif /* ntohl */ +#ifndef LWIP_PLATFORM_BYTESWAP +#define LWIP_PLATFORM_BYTESWAP 0 +#endif + #if BYTE_ORDER == BIG_ENDIAN #define htons(x) (x) #define ntohs(x) (x) #define htonl(x) (x) #define ntohl(x) (x) -#else +#else /* BYTE_ORDER != BIG_ENDIAN */ #ifdef LWIP_PREFIX_BYTEORDER_FUNCS /* workaround for naming collisions on some platforms */ #define htons lwip_htons #define ntohs lwip_ntohs #define htonl lwip_htonl #define ntohl lwip_ntohl -#endif -externC u16_t htons(u16_t x); -externC u16_t ntohs(u16_t x); -externC u32_t htonl(u32_t x); -externC u32_t ntohl(u32_t x); +#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */ +#if LWIP_PLATFORM_BYTESWAP +#define htons(x) LWIP_PLATFORM_HTONS(x) +#define ntohs(x) LWIP_PLATFORM_HTONS(x) +#define htonl(x) LWIP_PLATFORM_HTONL(x) +#define ntohl(x) LWIP_PLATFORM_HTONL(x) +#else /* LWIP_PLATFORM_BYTESWAP */ +u16_t htons(u16_t x); +u16_t ntohs(u16_t x); +u32_t htonl(u32_t x); +u32_t ntohl(u32_t x); +#endif /* LWIP_PLATFORM_BYTESWAP */ + +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +#ifdef __cplusplus +} #endif #endif /* __LWIP_INET_H__ */ - diff --git a/packages/net/lwip_tcpip/current/include/lwip/inet_chksum.h b/packages/net/lwip_tcpip/current/include/lwip/inet_chksum.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/inet_chksum.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ +#ifndef __LWIP_INET_CHKSUM_H__ +#define __LWIP_INET_CHKSUM_H__ + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len); +#if LWIP_UDPLITE +u16_t inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/packages/net/lwip_tcpip/current/include/lwip/init.h b/packages/net/lwip_tcpip/current/include/lwip/init.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/init.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ +#ifndef __LWIP_INIT_H__ +#define __LWIP_INIT_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** X.x.x: Major version of the stack */ +#define LWIP_VERSION_MAJOR 1U +/** x.X.x: Minor version of the stack */ +#define LWIP_VERSION_MINOR 3U +/** x.x.X: Revision of the stack */ +#define LWIP_VERSION_REVISION 2U +/** For release candidates, this is set to 1..254 + * For official releases, this is set to 255 (LWIP_RC_RELEASE) + * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */ +#define LWIP_VERSION_RC 255U + +/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ +#define LWIP_RC_RELEASE 255U +/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */ +#define LWIP_RC_DEVELOPMENT 0U + +#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE) +#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT) +#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT)) + +/** Provides the version of the stack */ +#define LWIP_VERSION (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \ + LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC) + +/* Modules initialization */ +void lwip_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/ip.h b/packages/net/lwip_tcpip/current/include/lwip/ip.h --- a/packages/net/lwip_tcpip/current/include/lwip/ip.h +++ b/packages/net/lwip_tcpip/current/include/lwip/ip.h @@ -32,30 +32,27 @@ #ifndef __LWIP_IP_H__ #define __LWIP_IP_H__ -#include "lwip/arch.h" +#include "lwip/opt.h" #include "lwip/def.h" #include "lwip/pbuf.h" #include "lwip/ip_addr.h" - #include "lwip/err.h" - +#include "lwip/netif.h" -void ip_init(void); -struct netif *ip_route(struct ip_addr *dest); -err_t ip_input(struct pbuf *p, struct netif *inp); -err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, - u8_t ttl, u8_t tos, u8_t proto); -err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, - u8_t ttl, u8_t tos, u8_t proto, - struct netif *netif); +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the function ip_output_if_opt() is only used with IGMP */ +#define IP_OPTIONS_SEND LWIP_IGMP #define IP_HLEN 20 -#define IP_PROTO_ICMP 1 -#define IP_PROTO_UDP 17 -#define IP_PROTO_UDPLITE 170 -#define IP_PROTO_TCP 6 +#define IP_PROTO_ICMP 1 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 /* This is passed as the destination address to ip_output_if (not to ip_output), meaning that an IP header already is constructed @@ -65,34 +62,47 @@ err_t ip_output_if(struct pbuf *p, struc #endif /* IP_HDRINCL */ #define IP_HDRINCL NULL +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ /* This is the common part of all PCB types. It needs to be at the beginning of a PCB type definition. It is located here so that changes to this common part are made in one location instead of having to change all PCB structs. */ -#define IP_PCB struct ip_addr local_ip; \ +#define IP_PCB \ + /* ip addresses in network byte order */ \ + struct ip_addr local_ip; \ struct ip_addr remote_ip; \ /* Socket options */ \ u16_t so_options; \ /* Type Of Service */ \ u8_t tos; \ /* Time To Live */ \ - u8_t ttl + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; /* * Option flags per-socket. These are the same like SO_XXX. */ -#define SOF_DEBUG (u16_t)0x0001U /* turn on debugging info recording */ -#define SOF_ACCEPTCONN (u16_t)0x0002U /* socket has had listen() */ -#define SOF_REUSEADDR (u16_t)0x0004U /* allow local address reuse */ -#define SOF_KEEPALIVE (u16_t)0x0008U /* keep connections alive */ -#define SOF_DONTROUTE (u16_t)0x0010U /* just use interface addresses */ -#define SOF_BROADCAST (u16_t)0x0020U /* permit sending of broadcast msgs */ -#define SOF_USELOOPBACK (u16_t)0x0040U /* bypass hardware when possible */ -#define SOF_LINGER (u16_t)0x0080U /* linger on close if data present */ -#define SOF_OOBINLINE (u16_t)0x0100U /* leave received OOB data in line */ -#define SOF_REUSEPORT (u16_t)0x0200U /* allow local address & port reuse */ - +#define SOF_DEBUG (u16_t)0x0001U /* turn on debugging info recording */ +#define SOF_ACCEPTCONN (u16_t)0x0002U /* socket has had listen() */ +#define SOF_REUSEADDR (u16_t)0x0004U /* allow local address reuse */ +#define SOF_KEEPALIVE (u16_t)0x0008U /* keep connections alive */ +#define SOF_DONTROUTE (u16_t)0x0010U /* just use interface addresses */ +#define SOF_BROADCAST (u16_t)0x0020U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +#define SOF_USELOOPBACK (u16_t)0x0040U /* bypass hardware when possible */ +#define SOF_LINGER (u16_t)0x0080U /* linger on close if data present */ +#define SOF_OOBINLINE (u16_t)0x0100U /* leave received OOB data in line */ +#define SOF_REUSEPORT (u16_t)0x0200U /* allow local address & port reuse */ #ifdef PACK_STRUCT_USE_INCLUDES @@ -139,16 +149,50 @@ PACK_STRUCT_END #define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) #define IPH_ID_SET(hdr, id) (hdr)->_id = (id) #define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) -#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl_proto = (htons(IPH_PROTO(hdr) | ((ttl) << 8))) +#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl_proto = (htons(IPH_PROTO(hdr) | ((u16_t)(ttl) << 8))) #define IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (IPH_TTL(hdr) << 8))) #define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) +/** The interface that provided the packet for the current callback invocation. */ +extern struct netif *current_netif; +/** Header of the input packet currently being processed. */ +extern const struct ip_hdr *current_header; + +#define ip_init() /* Compatibility define, not init needed. */ +struct netif *ip_route(struct ip_addr *dest); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, + struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if IP_OPTIONS_SEND +err_t ip_output_if_opt(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen); +#endif /* IP_OPTIONS_SEND */ +/** Get the interface that received the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_netif() (current_netif) +/** Get the IP header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_header() (current_header) #if IP_DEBUG void ip_debug_print(struct pbuf *p); #else #define ip_debug_print(p) #endif /* IP_DEBUG */ +#ifdef __cplusplus +} +#endif + #endif /* __LWIP_IP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h b/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h --- a/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h +++ b/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h @@ -32,7 +32,13 @@ #ifndef __LWIP_IP_ADDR_H__ #define __LWIP_IP_ADDR_H__ -#include "lwip/arch.h" +#include "lwip/opt.h" + +#include "lwip/inet.h" + +#ifdef __cplusplus +extern "C" { +#endif #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" @@ -46,6 +52,10 @@ PACK_STRUCT_END # include "arch/epstruct.h" #endif +/* + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif @@ -58,11 +68,6 @@ PACK_STRUCT_END # include "arch/epstruct.h" #endif -/* For compatibility with BSD code */ -struct in_addr { - u32_t s_addr; -}; - struct netif; extern const struct ip_addr ip_addr_any; @@ -71,48 +76,47 @@ extern const struct ip_addr ip_addr_broa /** IP_ADDR_ can be used as a fixed IP address * for the wildcard and the broadcast address */ -#define IP_ADDR_ANY ((struct ip_addr *)&ip_addr_any) -#define IP_ADDR_BROADCAST ((struct ip_addr *)&ip_addr_broadcast) - -#define INADDR_NONE ((u32_t) 0xffffffff) /* 255.255.255.255 */ -#define INADDR_LOOPBACK ((u32_t) 0x7f000001) /* 127.0.0.1 */ +#define IP_ADDR_ANY ((struct ip_addr *)&ip_addr_any) +#define IP_ADDR_BROADCAST ((struct ip_addr *)&ip_addr_broadcast) /* Definitions of the bits in an Internet address integer. On subnets, host and network parts are found according to the subnet mask, not these masks. */ -#define IN_CLASSA(a) ((((u32_t)(a)) & 0x80000000) == 0) -#define IN_CLASSA_NET 0xff000000 -#define IN_CLASSA_NSHIFT 24 -#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) -#define IN_CLASSA_MAX 128 +#define IN_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 -#define IN_CLASSB(a) ((((u32_t)(a)) & 0xc0000000) == 0x80000000) -#define IN_CLASSB_NET 0xffff0000 -#define IN_CLASSB_NSHIFT 16 -#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) -#define IN_CLASSB_MAX 65536 +#define IN_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 -#define IN_CLASSC(a) ((((u32_t)(a)) & 0xe0000000) == 0xc0000000) -#define IN_CLASSC_NET 0xffffff00 -#define IN_CLASSC_NSHIFT 8 -#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) +#define IN_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) -#define IN_CLASSD(a) (((u32_t)(a) & 0xf0000000) == 0xe0000000) -#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ -#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ -#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IN_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ #define IN_MULTICAST(a) IN_CLASSD(a) -#define IN_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000) == 0xf0000000) -#define IN_BADCLASS(a) (((u32_t)(a) & 0xf0000000) == 0xf0000000) +#define IN_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IN_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IN_LOOPBACKNET 127 /* official! */ -#define IN_LOOPBACKNET 127 /* official! */ - - -#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->addr = htonl(((u32_t)(a & 0xff) << 24) | ((u32_t)(b & 0xff) << 16) | \ - ((u32_t)(c & 0xff) << 8) | (u32_t)(d & 0xff)) +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = htonl(((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff)) #define ip_addr_set(dest, src) (dest)->addr = \ ((src) == NULL? 0:\ @@ -135,25 +139,35 @@ extern const struct ip_addr ip_addr_broa u8_t ip_addr_isbroadcast(struct ip_addr *, struct netif *); -#define ip_addr_ismulticast(addr1) (((addr1)->addr & ntohl(0xf0000000)) == ntohl(0xe0000000)) +#define ip_addr_ismulticast(addr1) (((addr1)->addr & ntohl(0xf0000000UL)) == ntohl(0xe0000000UL)) +#define ip_addr_islinklocal(addr1) (((addr1)->addr & ntohl(0xffff0000UL)) == ntohl(0xa9fe0000UL)) -#define ip_addr_debug_print(debug, ipaddr) LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ - ipaddr?(u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff:0, \ - ipaddr?(u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff:0, \ - ipaddr?(u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff:0, \ - ipaddr?(u16_t)ntohl((ipaddr)->addr) & 0xff:0U)) +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ + ipaddr != NULL ? \ + (u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff : 0, \ + ipaddr != NULL ? \ + (u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff : 0, \ + ipaddr != NULL ? \ + (u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff : 0, \ + ipaddr != NULL ? \ + (u16_t)ntohl((ipaddr)->addr) & 0xff : 0)) -/* cast to unsigned int, as it is used as argument to printf functions - * which expect integer arguments. CSi: use cc.h formatters (conversion chars)! */ +/* These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ #define ip4_addr1(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff) #define ip4_addr2(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff) #define ip4_addr3(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff) #define ip4_addr4(ipaddr) ((u16_t)(ntohl((ipaddr)->addr)) & 0xff) -#endif /* __LWIP_IP_ADDR_H__ */ - +/** + * Same as inet_ntoa() but takes a struct ip_addr* + */ +#define ip_ntoa(addr) ((addr != NULL) ? inet_ntoa(*((struct in_addr*)(addr))) : "NULL") - +#ifdef __cplusplus +} +#endif - +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/ip_frag.h b/packages/net/lwip_tcpip/current/include/lwip/ip_frag.h --- a/packages/net/lwip_tcpip/current/include/lwip/ip_frag.h +++ b/packages/net/lwip_tcpip/current/include/lwip/ip_frag.h @@ -33,15 +33,44 @@ #ifndef __LWIP_IP_FRAG_H__ #define __LWIP_IP_FRAG_H__ +#include "lwip/opt.h" #include "lwip/err.h" #include "lwip/pbuf.h" #include "lwip/netif.h" #include "lwip/ip_addr.h" +#include "lwip/ip.h" +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/* IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void); void ip_reass_tmr(void); struct pbuf * ip_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG err_t ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif #endif /* __LWIP_IP_FRAG_H__ */ - - diff --git a/packages/net/lwip_tcpip/current/include/lwip/mem.h b/packages/net/lwip_tcpip/current/include/lwip/mem.h --- a/packages/net/lwip_tcpip/current/include/lwip/mem.h +++ b/packages/net/lwip_tcpip/current/include/lwip/mem.h @@ -33,29 +33,75 @@ #define __LWIP_MEM_H__ #include "lwip/opt.h" -#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; +/* aliases for C library malloc() */ +#define mem_init() +/* in case C library malloc() needs extra protection, + * allow these defines to be overridden. + */ +#ifndef mem_free +#define mem_free free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#endif +#ifndef mem_realloc +static void *mem_realloc(void *mem, mem_size_t size) +{ + LWIP_UNUSED_ARG(size); + return mem; +} +#endif +#else /* MEM_LIBC_MALLOC */ + +/* MEM_SIZE would have to be aligned, but using 64000 here instead of + * 65535 leaves some room for alignment... + */ #if MEM_SIZE > 64000l typedef u32_t mem_size_t; #else typedef u16_t mem_size_t; #endif /* MEM_SIZE > 64000 */ - -void mem_init(void); - +#if MEM_USE_POOLS +/** mem_init is not used when using pools instead of a heap */ +#define mem_init() +/** mem_realloc is not used when using pools instead of a heap: + we can't free part of a pool element and don't want to copy the rest */ +#define mem_realloc(mem, size) (mem) +#else /* MEM_USE_POOLS */ +/* lwIP alternative malloc */ +void mem_init(void); +void *mem_realloc(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ void *mem_malloc(mem_size_t size); -void mem_free(void *mem); -void *mem_realloc(void *mem, mem_size_t size); -void *mem_reallocm(void *mem, mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#endif /* MEM_LIBC_MALLOC */ -#ifndef MEM_ALIGN_SIZE -#define MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) #endif -#ifndef MEM_ALIGN -#define MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#ifndef LWIP_MEM_ALIGN +#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#endif + +#ifdef __cplusplus +} #endif #endif /* __LWIP_MEM_H__ */ - diff --git a/packages/net/lwip_tcpip/current/include/lwip/memp.h b/packages/net/lwip_tcpip/current/include/lwip/memp.h --- a/packages/net/lwip_tcpip/current/include/lwip/memp.h +++ b/packages/net/lwip_tcpip/current/include/lwip/memp.h @@ -35,29 +35,82 @@ #include "lwip/opt.h" -typedef enum { - MEMP_PBUF, - MEMP_RAW_PCB, - MEMP_UDP_PCB, - MEMP_TCP_PCB, - MEMP_TCP_PCB_LISTEN, - MEMP_TCP_SEG, +#ifdef __cplusplus +extern "C" { +#endif - MEMP_NETBUF, - MEMP_NETCONN, - MEMP_API_MSG, - MEMP_TCPIP_MSG, - - MEMP_SYS_TIMEOUT, - +/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ +typedef enum { +#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, +#include "lwip/memp_std.h" MEMP_MAX } memp_t; -void memp_init(void); +#if MEM_USE_POOLS +/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ +typedef enum { + /* Get the first (via: + MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ + MEMP_POOL_HELPER_FIRST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START 1 +#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 +#define LWIP_MALLOC_MEMPOOL_END +#include "lwip/memp_std.h" + ) , + /* Get the last (via: + MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ + MEMP_POOL_HELPER_LAST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * +#define LWIP_MALLOC_MEMPOOL_END 1 +#include "lwip/memp_std.h" + ) +} memp_pool_helper_t; + +/* The actual start and stop values are here (cast them over) + We use this helper type and these defines so we can avoid using const memp_t values */ +#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) +#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) +#endif /* MEM_USE_POOLS */ +#if MEMP_MEM_MALLOC || MEM_USE_POOLS +extern const u16_t memp_sizes[MEMP_MAX]; +#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC + +#include "mem.h" + +#define memp_init() +#define memp_malloc(type) mem_malloc(memp_sizes[type]) +#define memp_free(type, mem) mem_free(mem) + +#else /* MEMP_MEM_MALLOC */ + +#if MEM_USE_POOLS +/** This structure is used to save the pool one element came from. */ +struct memp_malloc_helper +{ + memp_t poolnr; +}; +#endif /* MEM_USE_POOLS */ + +void memp_init(void); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else void *memp_malloc(memp_t type); -void *memp_realloc(memp_t fromtype, memp_t totype, void *mem); -void memp_free(memp_t type, void *mem); +#endif +void memp_free(memp_t type, void *mem); + +#endif /* MEMP_MEM_MALLOC */ -#endif /* __LWIP_MEMP_H__ */ - +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/memp_std.h b/packages/net/lwip_tcpip/current/include/lwip/memp_std.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/memp_std.h @@ -0,0 +1,102 @@ +/* + * SETUP: Make sure we define everything we will need. + * + * We have create three types of pools: + * 1) MEMPOOL - standard pools + * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c + * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct + * + * If the include'r doesn't require any special treatment of each of the types + * above, then will declare #2 & #3 to be just standard mempools. + */ +#ifndef LWIP_MALLOC_MEMPOOL +/* This treats "malloc pools" just like any other pool. + The pools are a little bigger to provide 'size' as the amount of user data. */ +#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL_END +#endif /* LWIP_MALLOC_MEMPOOL */ + +#ifndef LWIP_PBUF_MEMPOOL +/* This treats "pbuf pools" just like any other pool. + * Allocates buffers for a pbuf struct AND a payload size */ +#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#endif /* LWIP_PBUF_MEMPOOL */ + + +/* + * A list of internal pools used by LWIP. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ +#if LWIP_RAW +LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* IP_REASSEMBLY */ + +#if LWIP_NETCONN +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* NO_SYS==0 */ + +#if ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#endif /* LWIP_IGMP */ + +#if NO_SYS==0 +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") +#endif /* NO_SYS==0 */ + + +/* + * A list of pools of pbuf's used by LWIP. + * + * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + * This allocates enough space for the pbuf struct and a payload. + * (Example: pbuf_payload_size=0 allocates only size for the struct) + */ +LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * Allow for user-defined pools; this must be explicitly set in lwipopts.h + * since the default is to NOT look for lwippools.h + */ +#if MEMP_USE_CUSTOM_POOLS +#include "lwippools.h" +#endif /* MEMP_USE_CUSTOM_POOLS */ + +/* + * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later + * (#undef is ignored for something that is not defined) + */ +#undef LWIP_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL_START +#undef LWIP_MALLOC_MEMPOOL_END +#undef LWIP_PBUF_MEMPOOL diff --git a/packages/net/lwip_tcpip/current/include/lwip/netbuf.h b/packages/net/lwip_tcpip/current/include/lwip/netbuf.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/netbuf.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ +#ifndef __LWIP_NETBUF_H__ +#define __LWIP_NETBUF_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct netbuf { + struct pbuf *p, *ptr; + struct ip_addr *addr; + u16_t port; +#if LWIP_NETBUF_RECVINFO + struct ip_addr *toaddr; + u16_t toport; +#endif /* LWIP_NETBUF_RECVINFO */ +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +u16_t netbuf_len (struct netbuf *buf); +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#define netbuf_copy_partial(buf, dataptr, len, offset) \ + pbuf_copy_partial((buf)->p, (dataptr), (len), (offset)) +#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) +#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len) +#define netbuf_len(buf) ((buf)->p->tot_len) +#define netbuf_fromaddr(buf) ((buf)->addr) +#define netbuf_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) ((buf)->toaddr) +#define netbuf_destport(buf) ((buf)->toport) +#endif /* LWIP_NETBUF_RECVINFO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETBUF_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/netdb.h b/packages/net/lwip_tcpip/current/include/lwip/netdb.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/netdb.h @@ -0,0 +1,111 @@ +/* + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include /* for size_t */ + +#include "lwip/sockets.h" + +/* some rarely used options */ +#ifndef LWIP_DNS_API_DECLARE_H_ERRNO +#define LWIP_DNS_API_DECLARE_H_ERRNO 1 +#endif + +#ifndef LWIP_DNS_API_DEFINE_ERRORS +#define LWIP_DNS_API_DEFINE_ERRORS 1 +#endif + +#ifndef LWIP_DNS_API_DECLARE_STRUCTS +#define LWIP_DNS_API_DECLARE_STRUCTS 1 +#endif + +#if LWIP_DNS_API_DEFINE_ERRORS +/** Errors used by the DNS API functions, h_errno can be one of them */ +#define EAI_NONAME 200 +#define EAI_SERVICE 201 +#define EAI_FAIL 202 +#define EAI_MEMORY 203 + +#define HOST_NOT_FOUND 210 +#define NO_DATA 211 +#define NO_RECOVERY 212 +#define TRY_AGAIN 213 +#endif /* LWIP_DNS_API_DEFINE_ERRORS */ + +#if LWIP_DNS_API_DECLARE_STRUCTS +struct hostent { + char *h_name; /* Official name of the host. */ + char **h_aliases; /* A pointer to an array of pointers to alternative host names, + terminated by a null pointer. */ + int h_addrtype; /* Address type. */ + int h_length; /* The length, in bytes, of the address. */ + char **h_addr_list; /* A pointer to an array of pointers to network addresses (in + network byte order) for the host, terminated by a null pointer. */ +#define h_addr h_addr_list[0] /* for backward compatibility */ +}; + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Address family of socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol of socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address of socket. */ + char *ai_canonname; /* Canonical name of service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; +#endif /* LWIP_DNS_API_DECLARE_STRUCTS */ + +#if LWIP_DNS_API_DECLARE_H_ERRNO +/* application accessable error code set by the DNS API functions */ +extern int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/ + +struct hostent *lwip_gethostbyname(const char *name); +int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop); +void lwip_freeaddrinfo(struct addrinfo *ai); +int lwip_getaddrinfo(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); + +#if LWIP_COMPAT_SOCKETS +#define gethostbyname(name) lwip_gethostbyname(name) +#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \ + lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop) +#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo) +#define getaddrinfo(nodname, servname, hints, res) \ + lwip_getaddrinfo(nodname, servname, hints, res) +#endif /* LWIP_COMPAT_SOCKETS */ + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/netif.h b/packages/net/lwip_tcpip/current/include/lwip/netif.h --- a/packages/net/lwip_tcpip/current/include/lwip/netif.h +++ b/packages/net/lwip_tcpip/current/include/lwip/netif.h @@ -34,6 +34,8 @@ #include "lwip/opt.h" +#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + #include "lwip/err.h" #include "lwip/ip_addr.h" @@ -41,9 +43,19 @@ #include "lwip/inet.h" #include "lwip/pbuf.h" #if LWIP_DHCP -# include "lwip/dhcp.h" +struct dhcp; +#endif +#if LWIP_AUTOIP +struct autoip; #endif +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses are expected to be in + * the same byte order as in IP_PCB. */ + /** must be the maximum of all used hardware address lengths across all types of interfaces in use */ #define NETIF_MAX_HWADDR_LEN 6U @@ -54,16 +66,20 @@ * a software flag used to control whether this network * interface is enabled and processes traffic. */ -#define NETIF_FLAG_UP 0x1U +#define NETIF_FLAG_UP 0x01U /** if set, the netif has broadcast capability */ -#define NETIF_FLAG_BROADCAST 0x2U +#define NETIF_FLAG_BROADCAST 0x02U /** if set, the netif is one end of a point-to-point connection */ -#define NETIF_FLAG_POINTTOPOINT 0x4U +#define NETIF_FLAG_POINTTOPOINT 0x04U /** if set, the interface is configured using DHCP */ -#define NETIF_FLAG_DHCP 0x08U +#define NETIF_FLAG_DHCP 0x08U /** if set, the interface has an active link * (set by the network interface driver) */ -#define NETIF_FLAG_LINK_UP 0x10U +#define NETIF_FLAG_LINK_UP 0x10U +/** if set, the netif is an device using ARP */ +#define NETIF_FLAG_ETHARP 0x20U +/** if set, the netif has IGMP capability */ +#define NETIF_FLAG_IGMP 0x40U /** Generic data structure used for all lwIP network interfaces. * The following fields should be filled in by the initialization @@ -90,61 +106,158 @@ struct netif { * to send a packet on the interface. This function outputs * the pbuf as-is on the link medium. */ err_t (* linkoutput)(struct netif *netif, struct pbuf *p); +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + void (* status_callback)(struct netif *netif); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + void (* link_callback)(struct netif *netif); +#endif /* LWIP_NETIF_LINK_CALLBACK */ /** This field can be set by the device driver and could point * to state information for the device. */ void *state; #if LWIP_DHCP /** the DHCP client state information for this netif */ struct dhcp *dhcp; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /** the AutoIP client state information for this netif */ + struct autoip *autoip; #endif +#if LWIP_NETIF_HOSTNAME + /* the hostname for this netif, NULL is a valid value */ + char* hostname; +#endif /* LWIP_NETIF_HOSTNAME */ + /** maximum transfer unit (in bytes) */ + u16_t mtu; /** number of bytes used in hwaddr */ u8_t hwaddr_len; /** link level hardware address of this interface */ u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; - /** maximum transfer unit (in bytes) */ - u16_t mtu; /** flags (see NETIF_FLAG_ above) */ u8_t flags; - /** link type */ - u8_t link_type; /** descriptive abbreviation */ char name[2]; /** number of this interface */ u8_t num; +#if LWIP_SNMP + /** link type (from "snmp_ifType" enum from snmp.h) */ + u8_t link_type; + /** (estimate) link speed */ + u32_t link_speed; + /** timestamp at last change made (up/down) */ + u32_t ts; + /** counters */ + u32_t ifinoctets; + u32_t ifinucastpkts; + u32_t ifinnucastpkts; + u32_t ifindiscards; + u32_t ifoutoctets; + u32_t ifoutucastpkts; + u32_t ifoutnucastpkts; + u32_t ifoutdiscards; +#endif /* LWIP_SNMP */ +#if LWIP_IGMP + /* This function could be called to add or delete a entry in the multicast filter table of the ethernet MAC.*/ + err_t (*igmp_mac_filter)( struct netif *netif, struct ip_addr *group, u8_t action); +#endif /* LWIP_IGMP */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. */ + struct pbuf *loop_first; + struct pbuf *loop_last; +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t loop_cnt_current; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#endif /* ENABLE_LOOPBACK */ }; +#if LWIP_SNMP +#define NETIF_INIT_SNMP(netif, type, speed) \ + /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \ + netif->link_type = type; \ + /* your link speed here (units: bits per second) */ \ + netif->link_speed = speed; \ + netif->ts = 0; \ + netif->ifinoctets = 0; \ + netif->ifinucastpkts = 0; \ + netif->ifinnucastpkts = 0; \ + netif->ifindiscards = 0; \ + netif->ifoutoctets = 0; \ + netif->ifoutucastpkts = 0; \ + netif->ifoutnucastpkts = 0; \ + netif->ifoutdiscards = 0 +#else /* LWIP_SNMP */ +#define NETIF_INIT_SNMP(netif, type, speed) +#endif /* LWIP_SNMP */ + + /** The list of network interfaces. */ extern struct netif *netif_list; /** The default network interface. */ extern struct netif *netif_default; -/* netif_init() must be called first. */ -externC void netif_init(void); +#define netif_init() /* Compatibility define, not init needed. */ -externC struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, +struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw, void *state, err_t (* init)(struct netif *netif), err_t (* input)(struct pbuf *p, struct netif *netif)); -externC void +void netif_set_addr(struct netif *netif,struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw); -externC void netif_remove(struct netif * netif); +void netif_remove(struct netif * netif); /* Returns a network interface given its name. The name is of the form "et0", where the first two letters are the "name" field in the netif structure, and the digit is in the num field in the same structure. */ -externC struct netif *netif_find(char *name); +struct netif *netif_find(char *name); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr); +void netif_set_netmask(struct netif *netif, struct ip_addr *netmask); +void netif_set_gw(struct netif *netif, struct ip_addr *gw); -externC void netif_set_default(struct netif *netif); +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +u8_t netif_is_up(struct netif *netif); + +#if LWIP_NETIF_STATUS_CALLBACK +/* + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, void (* status_callback)(struct netif *netif)); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ -externC void netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr); -externC void netif_set_netmask(struct netif *netif, struct ip_addr *netmast); -externC void netif_set_gw(struct netif *netif, struct ip_addr *gw); -externC void netif_set_up(struct netif *netif); -externC void netif_set_down(struct netif *netif); -externC u8_t netif_is_up(struct netif *netif); +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +u8_t netif_is_link_up(struct netif *netif); +/* + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif)); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#ifdef __cplusplus +} +#endif + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p, struct ip_addr *dest_ip); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ #endif /* __LWIP_NETIF_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/netifapi.h b/packages/net/lwip_tcpip/current/include/lwip/netifapi.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/netifapi.h @@ -0,0 +1,105 @@ +/* + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#ifndef __LWIP_NETIFAPI_H__ +#define __LWIP_NETIFAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct netifapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + err_t err; + struct netif *netif; + union { + struct { + struct ip_addr *ipaddr; + struct ip_addr *netmask; + struct ip_addr *gw; + void *state; + err_t (* init) (struct netif *netif); + err_t (* input)(struct pbuf *p, struct netif *netif); + } add; + struct { + void (* voidfunc)(struct netif *netif); + err_t (* errtfunc)(struct netif *netif); + } common; + } msg; +}; + +struct netifapi_msg { + void (* function)(struct netifapi_msg_msg *msg); + struct netifapi_msg_msg msg; +}; + + +/* API for application */ +err_t netifapi_netif_add ( struct netif *netif, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif) ); + +err_t netifapi_netif_set_addr ( struct netif *netif, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw ); + +err_t netifapi_netif_common ( struct netif *netif, + void (* voidfunc)(struct netif *netif), + err_t (* errtfunc)(struct netif *netif) ); + +#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL) +#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL) +#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL) +#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL) +#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start) +#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL) +#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start) +#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETIF_API */ + +#endif /* __LWIP_NETIFAPI_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/opt.h b/packages/net/lwip_tcpip/current/include/lwip/opt.h --- a/packages/net/lwip_tcpip/current/include/lwip/opt.h +++ b/packages/net/lwip_tcpip/current/include/lwip/opt.h @@ -1,3 +1,9 @@ +/** + * @file + * + * lwIP Options Configuration + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -32,460 +38,1473 @@ #ifndef __LWIP_OPT_H__ #define __LWIP_OPT_H__ -/* Include user defined options first */ +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ #include "lwipopts.h" #include "lwip/debug.h" -/* Define default values for unconfigured parameters. */ - -/* Platform specific locking */ +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ -/* - * enable SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection - * for certain critical regions during buffer allocation, deallocation and memory +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory * allocation and deallocation. */ #ifndef SYS_LIGHTWEIGHT_PROT #define SYS_LIGHTWEIGHT_PROT 0 #endif +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ #ifndef NO_SYS #define NO_SYS 0 #endif -/* ---------- Memory options ---------- */ -/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which - lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 - byte alignment -> define MEM_ALIGNMENT to 2. */ + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#ifndef MEMCPY +#define MEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#ifndef SMEMCPY +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +#endif +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#ifndef MEM_LIBC_MALLOC +#define MEM_LIBC_MALLOC 0 +#endif + +/** +* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. +* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution +* speed and usage from interrupts! +*/ +#ifndef MEMP_MEM_MALLOC +#define MEMP_MEM_MALLOC 0 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> #define MEM_ALIGNMENT 4 + * 2 byte alignment -> #define MEM_ALIGNMENT 2 + */ #ifndef MEM_ALIGNMENT #define MEM_ALIGNMENT 1 #endif -/* MEM_SIZE: the size of the heap memory. If the application will send -a lot of data that needs to be copied, this should be set high. */ +/** + * MEM_SIZE: the size of the heap memory. If the application will send + * a lot of data that needs to be copied, this should be set high. + */ #ifndef MEM_SIZE #define MEM_SIZE 1600 #endif +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ #ifndef MEMP_SANITY_CHECK -#define MEMP_SANITY_CHECK 0 +#define MEMP_SANITY_CHECK 0 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#ifndef MEM_USE_POOLS +#define MEM_USE_POOLS 0 +#endif + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 #endif -/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application - sends a lot of data out of ROM (or other static memory), this - should be set high. */ +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * inlude path somewhere. + */ +#ifndef MEMP_USE_CUSTOM_POOLS +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ #ifndef MEMP_NUM_PBUF #define MEMP_NUM_PBUF 16 #endif -/* Number of raw connection PCBs */ +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ #ifndef MEMP_NUM_RAW_PCB #define MEMP_NUM_RAW_PCB 4 #endif -/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One - per active UDP "connection". */ +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ #ifndef MEMP_NUM_UDP_PCB #define MEMP_NUM_UDP_PCB 4 #endif -/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP - connections. */ + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ #ifndef MEMP_NUM_TCP_PCB #define MEMP_NUM_TCP_PCB 5 #endif -/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP - connections. */ + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ #ifndef MEMP_NUM_TCP_PCB_LISTEN #define MEMP_NUM_TCP_PCB_LISTEN 8 #endif -/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP - segments. */ + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ #ifndef MEMP_NUM_TCP_SEG #define MEMP_NUM_TCP_SEG 16 #endif -/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active - timeouts. */ + +/** + * MEMP_NUM_REASSDATA: the number of simultaneously IP packets queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 5 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#ifndef MEMP_NUM_ARP_QUEUE +#define MEMP_NUM_ARP_QUEUE 30 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members et the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#ifndef MEMP_NUM_IGMP_GROUP +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. + * (requires NO_SYS==0) + */ #ifndef MEMP_NUM_SYS_TIMEOUT #define MEMP_NUM_SYS_TIMEOUT 3 #endif -/* The following four are used only with the sequential API and can be - set to 0 if the application only will use the raw API. */ -/* MEMP_NUM_NETBUF: the number of struct netbufs. */ +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ #ifndef MEMP_NUM_NETBUF #define MEMP_NUM_NETBUF 2 #endif -/* MEMP_NUM_NETCONN: the number of struct netconns. */ + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ #ifndef MEMP_NUM_NETCONN #define MEMP_NUM_NETCONN 4 #endif -/* MEMP_NUM_APIMSG: the number of struct api_msg, used for - communication between the TCP/IP stack and the sequential - programs. */ -#ifndef MEMP_NUM_API_MSG -#define MEMP_NUM_API_MSG 8 -#endif -/* MEMP_NUM_TCPIPMSG: the number of struct tcpip_msg, which is used - for sequential API communication and incoming packets. Used in - src/api/tcpip.c. */ -#ifndef MEMP_NUM_TCPIP_MSG -#define MEMP_NUM_TCPIP_MSG 8 + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_API 8 #endif -/* ---------- Pbuf options ---------- */ -/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_INPKT +#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#endif +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ #ifndef PBUF_POOL_SIZE #define PBUF_POOL_SIZE 16 #endif -/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ - -#ifndef PBUF_POOL_BUFSIZE -#define PBUF_POOL_BUFSIZE 128 +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 #endif -/* PBUF_LINK_HLEN: the number of bytes that should be allocated for a - link level header. Defaults to 14 for Ethernet. */ - -#ifndef PBUF_LINK_HLEN -#define PBUF_LINK_HLEN 14 -#endif - - - -/* ---------- ARP options ---------- */ - -/** Number of active hardware address, IP address pairs cached */ +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ #ifndef ARP_TABLE_SIZE #define ARP_TABLE_SIZE 10 #endif /** - * If enabled, outgoing packets are queued during hardware address + * ARP_QUEUEING==1: Outgoing packets are queued during hardware address * resolution. - * - * This feature has not stabilized yet. Single-packet queueing is - * believed to be stable, multi-packet queueing is believed to - * clash with the TCP segment queueing. - * - * As multi-packet-queueing is currently disabled, enabling this - * _should_ work, but we need your testing feedback on lwip-users. - * */ #ifndef ARP_QUEUEING #define ARP_QUEUEING 1 #endif -/* This option is deprecated */ -#ifdef ETHARP_QUEUE_FIRST -#error ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h. +/** + * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be + * updated with the source MAC and IP addresses supplied in the packet. + * You may want to disable this if you do not trust LAN peers to have the + * correct addresses, or as a limited approach to attempt to handle + * spoofing. If disabled, lwIP will need to make a new ARP request if + * the peer is not already in the ARP table, adding a little latency. + */ +#ifndef ETHARP_TRUST_IP_MAC +#define ETHARP_TRUST_IP_MAC 1 #endif -/* This option is removed to comply with the ARP standard */ -#ifdef ETHARP_ALWAYS_INSERT -#error ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h. +/** + * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + */ +#ifndef ETHARP_SUPPORT_VLAN +#define ETHARP_SUPPORT_VLAN 0 #endif -/* ---------- IP options ---------- */ -/* Define IP_FORWARD to 1 if you wish to have the ability to forward - IP packets across network interfaces. If you are going to run lwIP - on a device with only one network interface, define this to 0. */ +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ #ifndef IP_FORWARD #define IP_FORWARD 0 #endif -/* If defined to 1, IP options are allowed (but not parsed). If - defined to 0, all packets with IP options are dropped. */ -#ifndef IP_OPTIONS -#define IP_OPTIONS 1 +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#ifndef IP_OPTIONS_ALLOWED +#define IP_OPTIONS_ALLOWED 1 #endif -/** IP reassembly and segmentation. Even if they both deal with IP - * fragments, note that these are orthogonal, one dealing with incoming - * packets, the other with outgoing packets +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. */ - -/** Reassemble incoming fragmented IP packets */ #ifndef IP_REASSEMBLY #define IP_REASSEMBLY 1 #endif -/** Fragment outgoing IP packets if their size exceeds MTU */ +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ #ifndef IP_FRAG #define IP_FRAG 1 #endif -/* ---------- ICMP options ---------- */ +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 10 +#endif -#ifndef ICMP_TTL -#define ICMP_TTL 255 +/** + * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP + * fragmentation. Otherwise pbufs are allocated and reference the original + * packet data to be fragmented. + */ +#ifndef IP_FRAG_USES_STATIC_BUF +#define IP_FRAG_USES_STATIC_BUF 1 +#endif + +/** + * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer + * (requires IP_FRAG_USES_STATIC_BUF==1) + */ +#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU) +#define IP_FRAG_MAX_MTU 1500 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#ifndef IP_DEFAULT_TTL +#define IP_DEFAULT_TTL 255 #endif -/* ---------- RAW options ---------- */ +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#ifndef IP_SOF_BROADCAST +#define IP_SOF_BROADCAST 0 +#endif + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#ifndef IP_SOF_BROADCAST_RECV +#define IP_SOF_BROADCAST_RECV 0 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#ifndef LWIP_ICMP +#define LWIP_ICMP 1 +#endif +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#ifndef ICMP_TTL +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#ifndef LWIP_BROADCAST_PING +#define LWIP_BROADCAST_PING 0 +#endif + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#ifndef LWIP_MULTICAST_PING +#define LWIP_MULTICAST_PING 0 +#endif + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ #ifndef LWIP_RAW #define LWIP_RAW 1 #endif +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ #ifndef RAW_TTL -#define RAW_TTL 255 +#define RAW_TTL (IP_DEFAULT_TTL) #endif -/* ---------- DHCP options ---------- */ - +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ #ifndef LWIP_DHCP #define LWIP_DHCP 0 #endif -/* 1 if you want to do an ARP check on the offered address - (recommended). */ +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ #ifndef DHCP_DOES_ARP_CHECK -#define DHCP_DOES_ARP_CHECK 1 +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP. This can be set + * as low as 1 to get an AutoIP address very quickly, but you should + * be prepared to handle a changing IP address when DHCP overrides + * AutoIP. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +#endif + +/* + ---------------------------------- + ---------- SNMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP + * transport. + */ +#ifndef LWIP_SNMP +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will + * allow. At least one request buffer is required. + */ +#ifndef SNMP_CONCURRENT_REQUESTS +#define SNMP_CONCURRENT_REQUESTS 1 +#endif + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#ifndef SNMP_TRAP_DESTINATIONS +#define SNMP_TRAP_DESTINATIONS 1 +#endif + +/** + * SNMP_PRIVATE_MIB: + */ +#ifndef SNMP_PRIVATE_MIB +#define SNMP_PRIVATE_MIB 0 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#ifndef SNMP_SAFE_REQUESTS +#define SNMP_SAFE_REQUESTS 1 #endif -/* ---------- UDP options ---------- */ +/* + ---------------------------------- + ---------- IGMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 0 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 0 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers */ +#ifndef DNS_MAX_SERVERS +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#ifndef DNS_DOES_NAME_CHECK +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** DNS use a local buffer if DNS_USES_STATIC_BUF=0, a static one if + DNS_USES_STATIC_BUF=1, or a dynamic one if DNS_USES_STATIC_BUF=2. + The buffer will be of size DNS_MSG_SIZE */ +#ifndef DNS_USES_STATIC_BUF +#define DNS_USES_STATIC_BUF 1 +#endif + +/** DNS message max. size. Default value is RFC compliant. */ +#ifndef DNS_MSG_SIZE +#define DNS_MSG_SIZE 512 +#endif + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, + * you have to define + * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}} + * (an array of structs name/address, where address is an u32_t in network + * byte order). + * + * Instead, you can also use an external function: + * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name) + * that returns the IP address or INADDR_NONE if not found. + */ +#ifndef DNS_LOCAL_HOSTLIST +#define DNS_LOCAL_HOSTLIST 0 +#endif /* DNS_LOCAL_HOSTLIST */ + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * LWIP_UDP==1: Turn on UDP. + */ #ifndef LWIP_UDP #define LWIP_UDP 1 #endif +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#ifndef LWIP_UDPLITE +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ #ifndef UDP_TTL -#define UDP_TTL 255 +#define UDP_TTL (IP_DEFAULT_TTL) #endif -/* ---------- TCP options ---------- */ +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#ifndef LWIP_NETBUF_RECVINFO +#define LWIP_NETBUF_RECVINFO 0 +#endif + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * LWIP_TCP==1: Turn on TCP. + */ #ifndef LWIP_TCP #define LWIP_TCP 1 #endif +/** + * TCP_TTL: Default Time-To-Live value. + */ #ifndef TCP_TTL -#define TCP_TTL 255 +#define TCP_TTL (IP_DEFAULT_TTL) #endif +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well + */ #ifndef TCP_WND -#define TCP_WND 2048 +#define TCP_WND (4 * TCP_MSS) #endif +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ #ifndef TCP_MAXRTX #define TCP_MAXRTX 12 #endif +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ #ifndef TCP_SYNMAXRTX #define TCP_SYNMAXRTX 6 #endif - -/* Controls if TCP should queue segments that arrive out of - order. Define to 0 if your device is low on memory. */ +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ #ifndef TCP_QUEUE_OOSEQ -#define TCP_QUEUE_OOSEQ 1 +#define TCP_QUEUE_OOSEQ (LWIP_TCP) #endif -/* TCP Maximum segment size. */ +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ #ifndef TCP_MSS -#define TCP_MSS 128 /* A *very* conservative default. */ +#define TCP_MSS 536 #endif -/* TCP sender buffer space (bytes). */ +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#ifndef TCP_CALCULATE_EFF_SEND_MSS +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + */ #ifndef TCP_SND_BUF #define TCP_SND_BUF 256 #endif -/* TCP sender buffer space (pbufs). This must be at least = 2 * - TCP_SND_BUF/TCP_MSS for things to work. */ +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ #ifndef TCP_SND_QUEUELEN -#define TCP_SND_QUEUELEN 4 * TCP_SND_BUF/TCP_MSS +#define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF)/(TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than or equal + * to TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable. + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT ((TCP_SND_BUF)/2) +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 0 #endif - -/* Maximum number of retransmissions of data segments. */ - -/* Maximum number of retransmissions of SYN segments. */ +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif -/* TCP writable space (bytes). This must be less than or equal - to TCP_SND_BUF. It is the amount of space which must be - available in the tcp snd_buf for select to return writable */ -#ifndef TCP_SNDLOWAT -#define TCP_SNDLOWAT TCP_SND_BUF/2 +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + */ +#ifndef LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_TIMESTAMPS 0 #endif -/* Support loop interface (127.0.0.1) */ -#ifndef LWIP_HAVE_LOOPIF -#define LWIP_HAVE_LOOPIF 0 +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#ifndef TCP_WND_UPDATE_THRESHOLD +#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4) #endif +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. + */ #ifndef LWIP_EVENT_API #define LWIP_EVENT_API 0 #define LWIP_CALLBACK_API 1 #else #define LWIP_EVENT_API 1 #define LWIP_CALLBACK_API 0 -#endif - -#ifndef LWIP_COMPAT_SOCKETS -#define LWIP_COMPAT_SOCKETS 1 #endif +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#ifndef PBUF_LINK_HLEN +#define PBUF_LINK_HLEN 14 +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accomodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#ifndef PBUF_POOL_BUFSIZE +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) +#endif + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquistion) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#ifndef LWIP_NETIF_LINK_CALLBACK +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#ifndef LWIP_NETIF_HWADDRHINT +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#ifndef LWIP_NETIF_LOOPBACK +#define LWIP_NETIF_LOOPBACK 0 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#ifndef LWIP_LOOPBACK_MAX_PBUFS +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#ifndef LWIP_NETIF_TX_SINGLE_PBUF +#define LWIP_NETIF_TX_SINGLE_PBUF 0 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c + */ +#ifndef LWIP_HAVE_LOOPIF +#define LWIP_HAVE_LOOPIF 0 +#endif + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +#ifndef LWIP_HAVE_SLIPIF +#define LWIP_HAVE_SLIPIF 0 +#endif + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#ifndef TCPIP_THREAD_NAME +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ #ifndef TCPIP_THREAD_PRIO #define TCPIP_THREAD_PRIO 1 #endif +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#ifndef TCPIP_MBOX_SIZE +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#ifndef SLIPIF_THREAD_NAME +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_STACKSIZE +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ #ifndef SLIPIF_THREAD_PRIO #define SLIPIF_THREAD_PRIO 1 #endif +/** + * PPP_THREAD_NAME: The name assigned to the pppMain thread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppMain" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppMain thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_STACKSIZE +#define PPP_THREAD_STACKSIZE 0 +#endif + +/** + * PPP_THREAD_PRIO: The priority assigned to the pppMain thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ #ifndef PPP_THREAD_PRIO #define PPP_THREAD_PRIO 1 #endif +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#ifndef DEFAULT_THREAD_NAME +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_STACKSIZE +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ #ifndef DEFAULT_THREAD_PRIO #define DEFAULT_THREAD_PRIO 1 #endif +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_RAW_RECVMBOX_SIZE +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif -/* ---------- Socket Options ---------- */ -/* Enable SO_REUSEADDR and SO_REUSEPORT options */ -#define SO_REUSE 0 -#if SO_REUSE -/* I removed the lot since this was an ugly hack. It broke the raw-API. - It also came with many ugly goto's, Christiaan Simons. */ -#error "SO_REUSE currently unavailable, this was a hack" -#endif +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_UDP_RECVMBOX_SIZE +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_TCP_RECVMBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#ifndef DEFAULT_ACCEPTMBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 0 +#endif + +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 1 +#endif +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names. + * (only used if you use sockets.c) + */ +#ifndef LWIP_COMPAT_SOCKETS +#define LWIP_COMPAT_SOCKETS 1 +#endif -/* ---------- Statistics options ---------- */ +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#ifndef LWIP_POSIX_SOCKETS_IO_NAMES +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#ifndef RECV_BUFSIZE_DEFAULT +#define RECV_BUFSIZE_DEFAULT INT_MAX +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR and SO_REUSEPORT options. DO NOT USE! + */ +#ifndef SO_REUSE +#define SO_REUSE 0 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ #ifndef LWIP_STATS #define LWIP_STATS 1 #endif #if LWIP_STATS +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ #ifndef LWIP_STATS_DISPLAY -#define LWIP_STATS_DISPLAY 0 +#define LWIP_STATS_DISPLAY 0 #endif +/** + * LINK_STATS==1: Enable link stats. + */ #ifndef LINK_STATS -#define LINK_STATS 1 +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#ifndef ETHARP_STATS +#define ETHARP_STATS (LWIP_ARP) #endif +/** + * IP_STATS==1: Enable IP stats. + */ #ifndef IP_STATS -#define IP_STATS 1 +#define IP_STATS 1 #endif +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ #ifndef IPFRAG_STATS -#define IPFRAG_STATS 1 +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) #endif +/** + * ICMP_STATS==1: Enable ICMP stats. + */ #ifndef ICMP_STATS -#define ICMP_STATS 1 +#define ICMP_STATS 1 #endif -#ifndef UDP_STATS -#define UDP_STATS 1 +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#ifndef IGMP_STATS +#define IGMP_STATS (LWIP_IGMP) #endif -#ifndef TCP_STATS -#define TCP_STATS 1 -#endif - -#ifndef MEM_STATS -#define MEM_STATS 1 +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#ifndef UDP_STATS +#define UDP_STATS (LWIP_UDP) #endif -#ifndef MEMP_STATS -#define MEMP_STATS 1 +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#ifndef TCP_STATS +#define TCP_STATS (LWIP_TCP) #endif -#ifndef PBUF_STATS -#define PBUF_STATS 1 +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#ifndef MEM_STATS +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) #endif -#ifndef SYS_STATS -#define SYS_STATS 1 +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#ifndef MEMP_STATS +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) #endif -#ifndef RAW_STATS -#define RAW_STATS 0 +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#ifndef SYS_STATS +#define SYS_STATS (NO_SYS == 0) #endif #else -#define LINK_STATS 0 -#define IP_STATS 0 -#define IPFRAG_STATS 0 -#define ICMP_STATS 0 -#define UDP_STATS 0 -#define TCP_STATS 0 -#define MEM_STATS 0 -#define MEMP_STATS 0 -#define PBUF_STATS 0 -#define SYS_STATS 0 -#define RAW_STATS 0 -#define LWIP_STATS_DISPLAY 0 +#define LINK_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 #endif /* LWIP_STATS */ -/* ---------- PPP options ---------- */ +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif -#ifndef PPP_SUPPORT -#define PPP_SUPPORT 0 /* Set for PPP */ +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT #endif -#if PPP_SUPPORT - -#define NUM_PPP 1 /* Max PPP sessions. */ +#if PPP_SUPPORT - - -#ifndef PAP_SUPPORT -#define PAP_SUPPORT 0 /* Set for PAP. */ +/** + * NUM_PPP: Max PPP sessions. + */ +#ifndef NUM_PPP +#define NUM_PPP 1 #endif +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ #ifndef CHAP_SUPPORT -#define CHAP_SUPPORT 0 /* Set for CHAP. */ +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 #endif -#define MSCHAP_SUPPORT 0 /* Set for MSCHAP (NOT FUNCTIONAL!) */ -#define CBCP_SUPPORT 0 /* Set for CBCP (NOT FUNCTIONAL!) */ -#define CCP_SUPPORT 0 /* Set for CCP (NOT FUNCTIONAL!) */ +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif -#ifndef VJ_SUPPORT -#define VJ_SUPPORT 0 /* Set for VJ header compression. */ +/** + * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 #endif -#ifndef MD5_SUPPORT -#define MD5_SUPPORT 0 /* Set for MD5 (see also CHAP) */ +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 0 #endif +/** + * MD5_SUPPORT==1: Support MD5 (see also CHAP). + */ +#ifndef MD5_SUPPORT +#define MD5_SUPPORT 0 +#endif /* - * Timeouts. + * Timeouts */ +#ifndef FSM_DEFTIMEOUT #define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef FSM_DEFMAXTERMREQS #define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXCONFREQS #define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ -#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif + +#ifndef UPAP_DEFTIMEOUT #define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ -#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif + +#ifndef CHAP_DEFTIMEOUT #define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef CHAP_DEFTRANSMITS #define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ - +#endif /* Interval in seconds between keepalive echo requests, 0 to disable. */ -#if 1 +#ifndef LCP_ECHOINTERVAL #define LCP_ECHOINTERVAL 0 -#else -#define LCP_ECHOINTERVAL 10 #endif /* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS #define LCP_MAXECHOFAILS 3 +#endif /* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG #define PPP_MAXIDLEFLAG 100 +#endif /* * Packet sizes @@ -496,177 +1515,326 @@ a lot of data that needs to be copied, t * of living in lcp.h) */ #define PPP_MTU 1500 /* Default MTU (size of Info field) */ -#if 0 -#define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) -#else +#ifndef PPP_MAXMTU +/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */ #define PPP_MAXMTU 1500 /* Largest MTU we allow */ #endif #define PPP_MINMTU 64 #define PPP_MRU 1500 /* default MRU = max length of info field */ #define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU #define PPP_DEFMRU 296 /* Try for this */ +#endif #define PPP_MINMRU 128 /* No MRUs below this */ - +#ifndef MAXNAMELEN #define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#endif +#ifndef MAXSECRETLEN #define MAXSECRETLEN 256 /* max length of password or secret */ +#endif #endif /* PPP_SUPPORT */ -/* checksum options - set to zero for hardware checksum support */ - +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ #ifndef CHECKSUM_GEN_IP #define CHECKSUM_GEN_IP 1 #endif +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ #ifndef CHECKSUM_GEN_UDP #define CHECKSUM_GEN_UDP 1 #endif +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ #ifndef CHECKSUM_GEN_TCP #define CHECKSUM_GEN_TCP 1 #endif +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ #ifndef CHECKSUM_CHECK_IP #define CHECKSUM_CHECK_IP 1 #endif +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ #ifndef CHECKSUM_CHECK_UDP #define CHECKSUM_CHECK_UDP 1 #endif +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ #ifndef CHECKSUM_CHECK_TCP #define CHECKSUM_CHECK_TCP 1 #endif -/* Debugging options all default to off */ - -#ifndef DBG_TYPES_ON -#define DBG_TYPES_ON 0 +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +#ifndef LWIP_DBG_MIN_LEVEL +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL #endif -#ifndef ETHARP_DEBUG -#define ETHARP_DEBUG DBG_OFF -#endif - -#ifndef NETIF_DEBUG -#define NETIF_DEBUG DBG_OFF +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#ifndef LWIP_DBG_TYPES_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON #endif -#ifndef PBUF_DEBUG -#define PBUF_DEBUG DBG_OFF +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#ifndef ETHARP_DEBUG +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#ifndef NETIF_DEBUG +#define NETIF_DEBUG LWIP_DBG_OFF #endif -#ifndef API_LIB_DEBUG -#define API_LIB_DEBUG DBG_OFF +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#ifndef PBUF_DEBUG +#define PBUF_DEBUG LWIP_DBG_OFF #endif -#ifndef API_MSG_DEBUG -#define API_MSG_DEBUG DBG_OFF +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#ifndef API_LIB_DEBUG +#define API_LIB_DEBUG LWIP_DBG_OFF #endif -#ifndef SOCKETS_DEBUG -#define SOCKETS_DEBUG DBG_OFF +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#ifndef API_MSG_DEBUG +#define API_MSG_DEBUG LWIP_DBG_OFF #endif -#ifndef ICMP_DEBUG -#define ICMP_DEBUG DBG_OFF +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#ifndef SOCKETS_DEBUG +#define SOCKETS_DEBUG LWIP_DBG_OFF #endif -#ifndef INET_DEBUG -#define INET_DEBUG DBG_OFF +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#ifndef ICMP_DEBUG +#define ICMP_DEBUG LWIP_DBG_OFF #endif -#ifndef IP_DEBUG -#define IP_DEBUG DBG_OFF +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#ifndef IGMP_DEBUG +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#ifndef INET_DEBUG +#define INET_DEBUG LWIP_DBG_OFF #endif -#ifndef IP_REASS_DEBUG -#define IP_REASS_DEBUG DBG_OFF +/** + * IP_DEBUG: Enable debugging for IP. + */ +#ifndef IP_DEBUG +#define IP_DEBUG LWIP_DBG_OFF #endif -#ifndef RAW_DEBUG -#define RAW_DEBUG DBG_OFF +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#ifndef IP_REASS_DEBUG +#define IP_REASS_DEBUG LWIP_DBG_OFF #endif -#ifndef MEM_DEBUG -#define MEM_DEBUG DBG_OFF +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#ifndef RAW_DEBUG +#define RAW_DEBUG LWIP_DBG_OFF #endif -#ifndef MEMP_DEBUG -#define MEMP_DEBUG DBG_OFF +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#ifndef MEM_DEBUG +#define MEM_DEBUG LWIP_DBG_OFF #endif -#ifndef SYS_DEBUG -#define SYS_DEBUG DBG_OFF +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#ifndef MEMP_DEBUG +#define MEMP_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_DEBUG -#define TCP_DEBUG DBG_OFF +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#ifndef SYS_DEBUG +#define SYS_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_INPUT_DEBUG -#define TCP_INPUT_DEBUG DBG_OFF +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#ifndef TCP_DEBUG +#define TCP_DEBUG LWIP_DBG_OFF #endif +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#ifndef TCP_INPUT_DEBUG +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ #ifndef TCP_FR_DEBUG -#define TCP_FR_DEBUG DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF #endif +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ #ifndef TCP_RTO_DEBUG -#define TCP_RTO_DEBUG DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_REXMIT_DEBUG -#define TCP_REXMIT_DEBUG DBG_OFF +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#ifndef TCP_CWND_DEBUG +#define TCP_CWND_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_CWND_DEBUG -#define TCP_CWND_DEBUG DBG_OFF +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#ifndef TCP_WND_DEBUG +#define TCP_WND_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_WND_DEBUG -#define TCP_WND_DEBUG DBG_OFF +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#ifndef TCP_OUTPUT_DEBUG +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_OUTPUT_DEBUG -#define TCP_OUTPUT_DEBUG DBG_OFF +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#ifndef TCP_RST_DEBUG +#define TCP_RST_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_RST_DEBUG -#define TCP_RST_DEBUG DBG_OFF +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#ifndef TCP_QLEN_DEBUG +#define TCP_QLEN_DEBUG LWIP_DBG_OFF #endif -#ifndef TCP_QLEN_DEBUG -#define TCP_QLEN_DEBUG DBG_OFF +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#ifndef UDP_DEBUG +#define UDP_DEBUG LWIP_DBG_OFF #endif -#ifndef UDP_DEBUG -#define UDP_DEBUG DBG_OFF +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#ifndef TCPIP_DEBUG +#define TCPIP_DEBUG LWIP_DBG_OFF #endif -#ifndef TCPIP_DEBUG -#define TCPIP_DEBUG DBG_OFF +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF #endif -#ifndef PPP_DEBUG -#define PPP_DEBUG DBG_OFF +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#ifndef SLIP_DEBUG +#define SLIP_DEBUG LWIP_DBG_OFF #endif -#ifndef SLIP_DEBUG -#define SLIP_DEBUG DBG_OFF +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#ifndef DHCP_DEBUG +#define DHCP_DEBUG LWIP_DBG_OFF #endif -#ifndef DHCP_DEBUG -#define DHCP_DEBUG DBG_OFF +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#ifndef AUTOIP_DEBUG +#define AUTOIP_DEBUG LWIP_DBG_OFF #endif +/** + * SNMP_MSG_DEBUG: Enable debugging for SNMP messages. + */ +#ifndef SNMP_MSG_DEBUG +#define SNMP_MSG_DEBUG LWIP_DBG_OFF +#endif -#ifndef DBG_MIN_LEVEL -#define DBG_MIN_LEVEL DBG_LEVEL_OFF +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#ifndef SNMP_MIB_DEBUG +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#ifndef DNS_DEBUG +#define DNS_DEBUG LWIP_DBG_OFF #endif #endif /* __LWIP_OPT_H__ */ - - - diff --git a/packages/net/lwip_tcpip/current/include/lwip/pbuf.h b/packages/net/lwip_tcpip/current/include/lwip/pbuf.h --- a/packages/net/lwip_tcpip/current/include/lwip/pbuf.h +++ b/packages/net/lwip_tcpip/current/include/lwip/pbuf.h @@ -33,8 +33,12 @@ #ifndef __LWIP_PBUF_H__ #define __LWIP_PBUF_H__ -#include "arch/cc.h" +#include "lwip/opt.h" +#include "lwip/err.h" +#ifdef __cplusplus +extern "C" { +#endif #define PBUF_TRANSPORT_HLEN 20 #define PBUF_IP_HLEN 20 @@ -47,21 +51,15 @@ typedef enum { } pbuf_layer; typedef enum { - PBUF_RAM, - PBUF_ROM, - PBUF_REF, - PBUF_POOL -} pbuf_flag; + PBUF_RAM, /* pbuf data is stored in RAM */ + PBUF_ROM, /* pbuf data is stored in ROM */ + PBUF_REF, /* pbuf comes from the pbuf pool */ + PBUF_POOL /* pbuf payload refers to RAM */ +} pbuf_type; -/* Definitions for the pbuf flag field. These are NOT the flags that - * are passed to pbuf_alloc(). */ -#define PBUF_FLAG_RAM 0x00U /* Flags that pbuf data is stored in RAM */ -#define PBUF_FLAG_ROM 0x01U /* Flags that pbuf data is stored in ROM */ -#define PBUF_FLAG_POOL 0x02U /* Flags that the pbuf comes from the pbuf pool */ -#define PBUF_FLAG_REF 0x04U /* Flags thet the pbuf payload refers to RAM */ -/** indicates this packet was broadcast on the link */ -#define PBUF_FLAG_LINK_BROADCAST 0x80U +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U struct pbuf { /** next pbuf in singly linked pbuf chain */ @@ -82,9 +80,12 @@ struct pbuf { /** length of this buffer */ u16_t len; - /** flags telling the type of pbuf, see PBUF_FLAG_ */ - u16_t flags; - + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + /** * the reference count always equals the number of pointers * that refer to this pbuf. This can be pointers from an application, @@ -94,20 +95,26 @@ struct pbuf { }; -void pbuf_init(void); +/* Initializes the pbuf module. This call is empty for now, but may not be in future. */ +#define pbuf_init() -struct pbuf *pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag); +struct pbuf *pbuf_alloc(pbuf_layer l, u16_t size, pbuf_type type); void pbuf_realloc(struct pbuf *p, u16_t size); u8_t pbuf_header(struct pbuf *p, s16_t header_size); void pbuf_ref(struct pbuf *p); void pbuf_ref_chain(struct pbuf *p); u8_t pbuf_free(struct pbuf *p); u8_t pbuf_clen(struct pbuf *p); -void pbuf_cat(struct pbuf *h, struct pbuf *t); -void pbuf_chain(struct pbuf *h, struct pbuf *t); -struct pbuf *pbuf_take(struct pbuf *f); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); struct pbuf *pbuf_dechain(struct pbuf *p); -void pbuf_queue(struct pbuf *p, struct pbuf *n); -struct pbuf * pbuf_dequeue(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); + +#ifdef __cplusplus +} +#endif #endif /* __LWIP_PBUF_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/raw.h b/packages/net/lwip_tcpip/current/include/lwip/raw.h --- a/packages/net/lwip_tcpip/current/include/lwip/raw.h +++ b/packages/net/lwip_tcpip/current/include/lwip/raw.h @@ -32,11 +32,18 @@ #ifndef __LWIP_RAW_H__ #define __LWIP_RAW_H__ -#include "lwip/arch.h" +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ #include "lwip/pbuf.h" #include "lwip/inet.h" #include "lwip/ip.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif struct raw_pcb { /* Common members of all PCB types */ @@ -44,16 +51,27 @@ struct raw_pcb { struct raw_pcb *next; - u16_t protocol; + u8_t protocol; + /* receive callback function + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ u8_t (* recv)(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr); + /* user-supplied argument for the recv callback */ void *recv_arg; }; /* The following functions is the application layer interface to the RAW code. */ -struct raw_pcb * raw_new (u16_t proto); +struct raw_pcb * raw_new (u8_t proto); void raw_remove (struct raw_pcb *pcb); err_t raw_bind (struct raw_pcb *pcb, struct ip_addr *ipaddr); err_t raw_connect (struct raw_pcb *pcb, struct ip_addr *ipaddr); @@ -63,12 +81,17 @@ void raw_recv (struct struct pbuf *p, struct ip_addr *addr), void *recv_arg); -err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr); err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); /* The following functions are the lower layer interface to RAW. */ -u8_t raw_input (struct pbuf *p, struct netif *inp); -void raw_init (void); +u8_t raw_input (struct pbuf *p, struct netif *inp); +#define raw_init() /* Compatibility define, not init needed. */ +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ #endif /* __LWIP_RAW_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/sio.h b/packages/net/lwip_tcpip/current/include/lwip/sio.h --- a/packages/net/lwip_tcpip/current/include/lwip/sio.h +++ b/packages/net/lwip_tcpip/current/include/lwip/sio.h @@ -32,32 +32,110 @@ * It needs to be implemented by those platforms which need SLIP or PPP */ -#include "arch/cc.h" +#ifndef __SIO_H__ +#define __SIO_H__ + +#include "lwip/arch.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* If you want to define sio_fd_t elsewhere or differently, + define this in your cc.h file. */ #ifndef __sio_fd_t_defined typedef void * sio_fd_t; #endif +/* The following functions can be defined to something else in your cc.h file + or be implemented in your custom sio.c file. */ + #ifndef sio_open -sio_fd_t sio_open(u8_t); +/** + * Opens a serial device for communication. + * + * @param devnum device number + * @return handle to serial device if successful, NULL otherwise + */ +sio_fd_t sio_open(u8_t devnum); #endif #ifndef sio_send -void sio_send(u8_t, sio_fd_t); +/** + * Sends a single character to the serial device. + * + * @param c character to send + * @param fd serial device handle + * + * @note This function will block until the character can be sent. + */ +void sio_send(u8_t c, sio_fd_t fd); #endif #ifndef sio_recv -u8_t sio_recv(sio_fd_t); +/** + * Receives a single character from the serial device. + * + * @param fd serial device handle + * + * @note This function will block until a character is received. + */ +u8_t sio_recv(sio_fd_t fd); #endif #ifndef sio_read -u32_t sio_read(sio_fd_t, u8_t *, u32_t); +/** + * Reads from the serial device. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received - may be 0 if aborted by sio_read_abort + * + * @note This function will block until data can be received. The blocking + * can be cancelled by calling sio_read_abort(). + */ +u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_tryread +/** + * Tries to read from the serial device. Same as sio_read but returns + * immediately if no data is available and never blocks. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received + */ +u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len); #endif #ifndef sio_write -u32_t sio_write(sio_fd_t, u8_t *, u32_t); +/** + * Writes to the serial device. + * + * @param fd serial device handle + * @param data pointer to data to send + * @param len length (in bytes) of data to send + * @return number of bytes actually sent + * + * @note This function will block until all data can be sent. + */ +u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len); #endif #ifndef sio_read_abort -void sio_read_abort(sio_fd_t); +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd); #endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SIO_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/snmp.h b/packages/net/lwip_tcpip/current/include/lwip/snmp.h --- a/packages/net/lwip_tcpip/current/include/lwip/snmp.h +++ b/packages/net/lwip_tcpip/current/include/lwip/snmp.h @@ -34,29 +34,115 @@ #define __LWIP_SNMP_H__ #include "lwip/opt.h" +#include "lwip/netif.h" +#include "lwip/udp.h" -/* SNMP support available? */ -#if defined(LWIP_SNMP) && (LWIP_SNMP > 0) +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @see RFC1213, "MIB-II, 6. Definitions" + */ +enum snmp_ifType { + snmp_ifType_other=1, /* none of the following */ + snmp_ifType_regular1822, + snmp_ifType_hdh1822, + snmp_ifType_ddn_x25, + snmp_ifType_rfc877_x25, + snmp_ifType_ethernet_csmacd, + snmp_ifType_iso88023_csmacd, + snmp_ifType_iso88024_tokenBus, + snmp_ifType_iso88025_tokenRing, + snmp_ifType_iso88026_man, + snmp_ifType_starLan, + snmp_ifType_proteon_10Mbit, + snmp_ifType_proteon_80Mbit, + snmp_ifType_hyperchannel, + snmp_ifType_fddi, + snmp_ifType_lapb, + snmp_ifType_sdlc, + snmp_ifType_ds1, /* T-1 */ + snmp_ifType_e1, /* european equiv. of T-1 */ + snmp_ifType_basicISDN, + snmp_ifType_primaryISDN, /* proprietary serial */ + snmp_ifType_propPointToPointSerial, + snmp_ifType_ppp, + snmp_ifType_softwareLoopback, + snmp_ifType_eon, /* CLNP over IP [11] */ + snmp_ifType_ethernet_3Mbit, + snmp_ifType_nsip, /* XNS over IP */ + snmp_ifType_slip, /* generic SLIP */ + snmp_ifType_ultra, /* ULTRA technologies */ + snmp_ifType_ds3, /* T-3 */ + snmp_ifType_sip, /* SMDS */ + snmp_ifType_frame_relay +}; + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** SNMP "sysuptime" Interval */ +#define SNMP_SYSUPTIME_INTERVAL 10 + +/** fixed maximum length for object identifier type */ +#define LWIP_SNMP_OBJ_ID_LEN 32 + +/** internal object identifier representation */ +struct snmp_obj_id +{ + u8_t len; + s32_t id[LWIP_SNMP_OBJ_ID_LEN]; +}; + +/* system */ +void snmp_set_sysdesr(u8_t* str, u8_t* len); +void snmp_set_sysobjid(struct snmp_obj_id *oid); +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid); +void snmp_inc_sysuptime(void); +void snmp_add_sysuptime(u32_t value); +void snmp_get_sysuptime(u32_t *value); +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen); /* network interface */ -void snmp_add_ifinoctets(unsigned long value); -void snmp_inc_ifinucastpkts(void); -void snmp_inc_ifinnucastpkts(void); -void snmp_inc_ifindiscards(void); -void snmp_add_ifoutoctets(unsigned long value); -void snmp_inc_ifoutucastpkts(void); -void snmp_inc_ifoutnucastpkts(void); -void snmp_inc_ifoutdiscards(void); +void snmp_add_ifinoctets(struct netif *ni, u32_t value); +void snmp_inc_ifinucastpkts(struct netif *ni); +void snmp_inc_ifinnucastpkts(struct netif *ni); +void snmp_inc_ifindiscards(struct netif *ni); +void snmp_add_ifoutoctets(struct netif *ni, u32_t value); +void snmp_inc_ifoutucastpkts(struct netif *ni); +void snmp_inc_ifoutnucastpkts(struct netif *ni); +void snmp_inc_ifoutdiscards(struct netif *ni); +void snmp_inc_iflist(void); +void snmp_dec_iflist(void); + +/* ARP (for atTable and ipNetToMediaTable) */ +void snmp_insert_arpidx_tree(struct netif *ni, struct ip_addr *ip); +void snmp_delete_arpidx_tree(struct netif *ni, struct ip_addr *ip); /* IP */ void snmp_inc_ipinreceives(void); -void snmp_inc_ipindelivers(void); +void snmp_inc_ipinhdrerrors(void); +void snmp_inc_ipinaddrerrors(void); +void snmp_inc_ipforwdatagrams(void); +void snmp_inc_ipinunknownprotos(void); void snmp_inc_ipindiscards(void); -void snmp_inc_ipoutdiscards(void); +void snmp_inc_ipindelivers(void); void snmp_inc_ipoutrequests(void); -void snmp_inc_ipunknownprotos(void); -void snmp_inc_ipnoroutes(void); -void snmp_inc_ipforwdatagrams(void); +void snmp_inc_ipoutdiscards(void); +void snmp_inc_ipoutnoroutes(void); +void snmp_inc_ipreasmreqds(void); +void snmp_inc_ipreasmoks(void); +void snmp_inc_ipreasmfails(void); +void snmp_inc_ipfragoks(void); +void snmp_inc_ipfragfails(void); +void snmp_inc_ipfragcreates(void); +void snmp_inc_iproutingdiscards(void); +void snmp_insert_ipaddridx_tree(struct netif *ni); +void snmp_delete_ipaddridx_tree(struct netif *ni); +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni); +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni); /* ICMP */ void snmp_inc_icmpinmsgs(void); @@ -91,7 +177,6 @@ void snmp_inc_tcpactiveopens(void); void snmp_inc_tcppassiveopens(void); void snmp_inc_tcpattemptfails(void); void snmp_inc_tcpestabresets(void); -void snmp_inc_tcpcurrestab(void); void snmp_inc_tcpinsegs(void); void snmp_inc_tcpoutsegs(void); void snmp_inc_tcpretranssegs(void); @@ -103,30 +188,94 @@ void snmp_inc_udpindatagrams(void); void snmp_inc_udpnoports(void); void snmp_inc_udpinerrors(void); void snmp_inc_udpoutdatagrams(void); +void snmp_insert_udpidx_tree(struct udp_pcb *pcb); +void snmp_delete_udpidx_tree(struct udp_pcb *pcb); + +/* SNMP */ +void snmp_inc_snmpinpkts(void); +void snmp_inc_snmpoutpkts(void); +void snmp_inc_snmpinbadversions(void); +void snmp_inc_snmpinbadcommunitynames(void); +void snmp_inc_snmpinbadcommunityuses(void); +void snmp_inc_snmpinasnparseerrs(void); +void snmp_inc_snmpintoobigs(void); +void snmp_inc_snmpinnosuchnames(void); +void snmp_inc_snmpinbadvalues(void); +void snmp_inc_snmpinreadonlys(void); +void snmp_inc_snmpingenerrs(void); +void snmp_add_snmpintotalreqvars(u8_t value); +void snmp_add_snmpintotalsetvars(u8_t value); +void snmp_inc_snmpingetrequests(void); +void snmp_inc_snmpingetnexts(void); +void snmp_inc_snmpinsetrequests(void); +void snmp_inc_snmpingetresponses(void); +void snmp_inc_snmpintraps(void); +void snmp_inc_snmpouttoobigs(void); +void snmp_inc_snmpoutnosuchnames(void); +void snmp_inc_snmpoutbadvalues(void); +void snmp_inc_snmpoutgenerrs(void); +void snmp_inc_snmpoutgetrequests(void); +void snmp_inc_snmpoutgetnexts(void); +void snmp_inc_snmpoutsetrequests(void); +void snmp_inc_snmpoutgetresponses(void); +void snmp_inc_snmpouttraps(void); +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid); +void snmp_set_snmpenableauthentraps(u8_t *value); +void snmp_get_snmpenableauthentraps(u8_t *value); /* LWIP_SNMP support not available */ /* define everything to be empty */ #else +/* system */ +#define snmp_set_sysdesr(str, len) +#define snmp_set_sysobjid(oid); +#define snmp_get_sysobjid_ptr(oid) +#define snmp_inc_sysuptime() +#define snmp_add_sysuptime(value) +#define snmp_get_sysuptime(value) +#define snmp_set_syscontact(ocstr, ocstrlen); +#define snmp_set_sysname(ocstr, ocstrlen); +#define snmp_set_syslocation(ocstr, ocstrlen); + /* network interface */ -#define snmp_add_ifinoctets(value) -#define snmp_inc_ifinucastpkts() -#define snmp_inc_ifinnucastpkts() -#define snmp_inc_ifindiscards() -#define snmp_add_ifoutoctets(value) -#define snmp_inc_ifoutucastpkts() -#define snmp_inc_ifoutnucastpkts() -#define snmp_inc_ifoutdiscards() +#define snmp_add_ifinoctets(ni,value) +#define snmp_inc_ifinucastpkts(ni) +#define snmp_inc_ifinnucastpkts(ni) +#define snmp_inc_ifindiscards(ni) +#define snmp_add_ifoutoctets(ni,value) +#define snmp_inc_ifoutucastpkts(ni) +#define snmp_inc_ifoutnucastpkts(ni) +#define snmp_inc_ifoutdiscards(ni) +#define snmp_inc_iflist() +#define snmp_dec_iflist() + +/* ARP */ +#define snmp_insert_arpidx_tree(ni,ip) +#define snmp_delete_arpidx_tree(ni,ip) /* IP */ #define snmp_inc_ipinreceives() -#define snmp_inc_ipindelivers() +#define snmp_inc_ipinhdrerrors() +#define snmp_inc_ipinaddrerrors() +#define snmp_inc_ipforwdatagrams() +#define snmp_inc_ipinunknownprotos() #define snmp_inc_ipindiscards() -#define snmp_inc_ipoutdiscards() +#define snmp_inc_ipindelivers() #define snmp_inc_ipoutrequests() -#define snmp_inc_ipunknownprotos() -#define snmp_inc_ipnoroutes() -#define snmp_inc_ipforwdatagrams() +#define snmp_inc_ipoutdiscards() +#define snmp_inc_ipoutnoroutes() +#define snmp_inc_ipreasmreqds() +#define snmp_inc_ipreasmoks() +#define snmp_inc_ipreasmfails() +#define snmp_inc_ipfragoks() +#define snmp_inc_ipfragfails() +#define snmp_inc_ipfragcreates() +#define snmp_inc_iproutingdiscards() +#define snmp_insert_ipaddridx_tree(ni) +#define snmp_delete_ipaddridx_tree(ni) +#define snmp_insert_iprteidx_tree(dflt, ni) +#define snmp_delete_iprteidx_tree(dflt, ni) /* ICMP */ #define snmp_inc_icmpinmsgs() @@ -160,7 +309,6 @@ void snmp_inc_udpoutdatagrams(void); #define snmp_inc_tcppassiveopens() #define snmp_inc_tcpattemptfails() #define snmp_inc_tcpestabresets() -#define snmp_inc_tcpcurrestab() #define snmp_inc_tcpinsegs() #define snmp_inc_tcpoutsegs() #define snmp_inc_tcpretranssegs() @@ -172,7 +320,45 @@ void snmp_inc_udpoutdatagrams(void); #define snmp_inc_udpnoports() #define snmp_inc_udpinerrors() #define snmp_inc_udpoutdatagrams() +#define snmp_insert_udpidx_tree(pcb) +#define snmp_delete_udpidx_tree(pcb) +/* SNMP */ +#define snmp_inc_snmpinpkts() +#define snmp_inc_snmpoutpkts() +#define snmp_inc_snmpinbadversions() +#define snmp_inc_snmpinbadcommunitynames() +#define snmp_inc_snmpinbadcommunityuses() +#define snmp_inc_snmpinasnparseerrs() +#define snmp_inc_snmpintoobigs() +#define snmp_inc_snmpinnosuchnames() +#define snmp_inc_snmpinbadvalues() +#define snmp_inc_snmpinreadonlys() +#define snmp_inc_snmpingenerrs() +#define snmp_add_snmpintotalreqvars(value) +#define snmp_add_snmpintotalsetvars(value) +#define snmp_inc_snmpingetrequests() +#define snmp_inc_snmpingetnexts() +#define snmp_inc_snmpinsetrequests() +#define snmp_inc_snmpingetresponses() +#define snmp_inc_snmpintraps() +#define snmp_inc_snmpouttoobigs() +#define snmp_inc_snmpoutnosuchnames() +#define snmp_inc_snmpoutbadvalues() +#define snmp_inc_snmpoutgenerrs() +#define snmp_inc_snmpoutgetrequests() +#define snmp_inc_snmpoutgetnexts() +#define snmp_inc_snmpoutsetrequests() +#define snmp_inc_snmpoutgetresponses() +#define snmp_inc_snmpouttraps() +#define snmp_get_snmpgrpid_ptr(oid) +#define snmp_set_snmpenableauthentraps(value) +#define snmp_get_snmpenableauthentraps(value) + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} #endif #endif /* __LWIP_SNMP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/snmp_asn1.h b/packages/net/lwip_tcpip/current/include/lwip/snmp_asn1.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/snmp_asn1.h @@ -0,0 +1,101 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) codec. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_ASN1_H__ +#define __LWIP_SNMP_ASN1_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/snmp.h" + +#if LWIP_SNMP + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNMP_ASN1_UNIV (!0x80 | !0x40) +#define SNMP_ASN1_APPLIC (!0x80 | 0x40) +#define SNMP_ASN1_CONTXT ( 0x80 | !0x40) + +#define SNMP_ASN1_CONSTR (0x20) +#define SNMP_ASN1_PRIMIT (!0x20) + +/* universal tags */ +#define SNMP_ASN1_INTEG 2 +#define SNMP_ASN1_OC_STR 4 +#define SNMP_ASN1_NUL 5 +#define SNMP_ASN1_OBJ_ID 6 +#define SNMP_ASN1_SEQ 16 + +/* application specific (SNMP) tags */ +#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */ +#define SNMP_ASN1_COUNTER 1 /* u32_t */ +#define SNMP_ASN1_GAUGE 2 /* u32_t */ +#define SNMP_ASN1_TIMETICKS 3 /* u32_t */ +#define SNMP_ASN1_OPAQUE 4 /* octet string */ + +/* context specific (SNMP) tags */ +#define SNMP_ASN1_PDU_GET_REQ 0 +#define SNMP_ASN1_PDU_GET_NEXT_REQ 1 +#define SNMP_ASN1_PDU_GET_RESP 2 +#define SNMP_ASN1_PDU_SET_REQ 3 +#define SNMP_ASN1_PDU_TRAP 4 + +err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type); +err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length); +err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value); +err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value); +err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid); +err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw); + +void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed); +void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed); +void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed); +void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed); +err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type); +err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length); +err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, u32_t value); +err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, s32_t value); +err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident); +err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u8_t raw_len, u8_t *raw); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_ASN1_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/snmp_msg.h b/packages/net/lwip_tcpip/current/include/lwip/snmp_msg.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/snmp_msg.h @@ -0,0 +1,311 @@ +/** + * @file + * SNMP Agent message handling structures. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_MSG_H__ +#define __LWIP_SNMP_MSG_H__ + +#include "lwip/opt.h" +#include "lwip/snmp.h" +#include "lwip/snmp_structs.h" + +#if LWIP_SNMP + +#if SNMP_PRIVATE_MIB +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +#define SNMP_ES_NOERROR 0 +#define SNMP_ES_TOOBIG 1 +#define SNMP_ES_NOSUCHNAME 2 +#define SNMP_ES_BADVALUE 3 +#define SNMP_ES_READONLY 4 +#define SNMP_ES_GENERROR 5 + +#define SNMP_GENTRAP_COLDSTART 0 +#define SNMP_GENTRAP_WARMSTART 1 +#define SNMP_GENTRAP_AUTHFAIL 4 +#define SNMP_GENTRAP_ENTERPRISESPC 6 + +struct snmp_varbind +{ + /* next pointer, NULL for last in list */ + struct snmp_varbind *next; + /* previous pointer, NULL for first in list */ + struct snmp_varbind *prev; + + /* object identifier length (in s32_t) */ + u8_t ident_len; + /* object identifier array */ + s32_t *ident; + + /* object value ASN1 type */ + u8_t value_type; + /* object value length (in u8_t) */ + u8_t value_len; + /* object value */ + void *value; + + /* encoding varbind seq length length */ + u8_t seqlenlen; + /* encoding object identifier length length */ + u8_t olenlen; + /* encoding object value length length */ + u8_t vlenlen; + /* encoding varbind seq length */ + u16_t seqlen; + /* encoding object identifier length */ + u16_t olen; + /* encoding object value length */ + u16_t vlen; +}; + +struct snmp_varbind_root +{ + struct snmp_varbind *head; + struct snmp_varbind *tail; + /* number of variable bindings in list */ + u8_t count; + /* encoding varbind-list seq length length */ + u8_t seqlenlen; + /* encoding varbind-list seq length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_resp_header_lengths +{ + /* encoding error-index length length */ + u8_t erridxlenlen; + /* encoding error-status length length */ + u8_t errstatlenlen; + /* encoding request id length length */ + u8_t ridlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding error-index length */ + u16_t erridxlen; + /* encoding error-status length */ + u16_t errstatlen; + /* encoding request id length */ + u16_t ridlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_trap_header_lengths +{ + /* encoding timestamp length length */ + u8_t tslenlen; + /* encoding specific-trap length length */ + u8_t strplenlen; + /* encoding generic-trap length length */ + u8_t gtrplenlen; + /* encoding agent-addr length length */ + u8_t aaddrlenlen; + /* encoding enterprise-id length length */ + u8_t eidlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding timestamp length */ + u16_t tslen; + /* encoding specific-trap length */ + u16_t strplen; + /* encoding generic-trap length */ + u16_t gtrplen; + /* encoding agent-addr length */ + u16_t aaddrlen; + /* encoding enterprise-id length */ + u16_t eidlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/* Accepting new SNMP messages. */ +#define SNMP_MSG_EMPTY 0 +/* Search for matching object for variable binding. */ +#define SNMP_MSG_SEARCH_OBJ 1 +/* Perform SNMP operation on in-memory object. + Pass-through states, for symmetry only. */ +#define SNMP_MSG_INTERNAL_GET_OBJDEF 2 +#define SNMP_MSG_INTERNAL_GET_VALUE 3 +#define SNMP_MSG_INTERNAL_SET_TEST 4 +#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5 +#define SNMP_MSG_INTERNAL_SET_VALUE 6 +/* Perform SNMP operation on object located externally. + In theory this could be used for building a proxy agent. + Practical use is for an enterprise spc. app. gateway. */ +#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7 +#define SNMP_MSG_EXTERNAL_GET_VALUE 8 +#define SNMP_MSG_EXTERNAL_SET_TEST 9 +#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10 +#define SNMP_MSG_EXTERNAL_SET_VALUE 11 + +#define SNMP_COMMUNITY_STR_LEN 64 +struct snmp_msg_pstat +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* source IP address */ + struct ip_addr sip; + /* source UDP port */ + u16_t sp; + /* request type */ + u8_t rt; + /* request ID */ + s32_t rid; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* community name (zero terminated) */ + u8_t community[SNMP_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u8_t com_strlen; + /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */ + u8_t state; + /* saved arguments for MSG_EXTERNAL_x */ + struct mib_external_node *ext_mib_node; + struct snmp_name_ptr ext_name_ptr; + struct obj_def ext_object_def; + struct snmp_obj_id ext_oid; + /* index into input variable binding list */ + u8_t vb_idx; + /* ptr into input variable binding list */ + struct snmp_varbind *vb_ptr; + /* list of variable bindings from input */ + struct snmp_varbind_root invb; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output response lengths used in ASN encoding */ + struct snmp_resp_header_lengths rhl; +}; + +struct snmp_msg_trap +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* destination IP address in network order */ + struct ip_addr dip; + + /* source enterprise ID (sysObjectID) */ + struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + u8_t sip_raw[4]; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output trap lengths used in ASN encoding */ + struct snmp_trap_header_lengths thl; +}; + +/** Agent Version constant, 0 = v1 oddity */ +extern const s32_t snmp_version; +/** Agent default "public" community string */ +extern const char snmp_publiccommunity[7]; + +extern struct snmp_msg_trap trap_msg; + +/** Agent setup, start listening to port 161. */ +void snmp_init(void); +void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable); +void snmp_trap_dst_ip_set(u8_t dst_idx, struct ip_addr *dst); + +/** Varbind-list functions. */ +struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len); +void snmp_varbind_free(struct snmp_varbind *vb); +void snmp_varbind_list_free(struct snmp_varbind_root *root); +void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb); +struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root); + +/** Handle an internal (recv) or external (private response) event. */ +void snmp_msg_event(u8_t request_id); +err_t snmp_send_response(struct snmp_msg_pstat *m_stat); +err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap); +void snmp_coldstart_trap(void); +void snmp_authfail_trap(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_MSG_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/snmp_structs.h b/packages/net/lwip_tcpip/current/include/lwip/snmp_structs.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/lwip/snmp_structs.h @@ -0,0 +1,262 @@ +/** + * @file + * Generic MIB tree structures. + * + * @todo namespace prefixes + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_STRUCTS_H__ +#define __LWIP_SNMP_STRUCTS_H__ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" + +#if SNMP_PRIVATE_MIB +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* MIB object instance */ +#define MIB_OBJECT_NONE 0 +#define MIB_OBJECT_SCALAR 1 +#define MIB_OBJECT_TAB 2 + +/* MIB object access */ +#define MIB_OBJECT_READ_ONLY 0 +#define MIB_OBJECT_READ_WRITE 1 +#define MIB_OBJECT_WRITE_ONLY 2 +#define MIB_OBJECT_NOT_ACCESSIBLE 3 + +/** object definition returned by (get_object_def)() */ +struct obj_def +{ + /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */ + u8_t instance; + /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */ + u8_t access; + /* ASN type for this object */ + u8_t asn_type; + /* value length (host length) */ + u16_t v_len; + /* length of instance part of supplied object identifier */ + u8_t id_inst_len; + /* instance part of supplied object identifier */ + s32_t *id_inst_ptr; +}; + +struct snmp_name_ptr +{ + u8_t ident_len; + s32_t *ident; +}; + +/** MIB const scalar (.0) node */ +#define MIB_NODE_SC 0x01 +/** MIB const array node */ +#define MIB_NODE_AR 0x02 +/** MIB array node (mem_malloced from RAM) */ +#define MIB_NODE_RA 0x03 +/** MIB list root node (mem_malloced from RAM) */ +#define MIB_NODE_LR 0x04 +/** MIB node for external objects */ +#define MIB_NODE_EX 0x05 + +/** node "base class" layout, the mandatory fields for a node */ +struct mib_node +{ + /** returns struct obj_def for the given object identifier */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + /** returns object value for the given object identifier, + @note the caller must allocate at least len bytes for the value */ + void (*get_value)(struct obj_def *od, u16_t len, void *value); + /** tests length and/or range BEFORE setting */ + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + /** sets object value, only to be called when set_test() */ + void (*set_value)(struct obj_def *od, u16_t len, void *value); + /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */ + const u8_t node_type; + /* array or max list length */ + const u16_t maxlength; +}; + +/** derived node for scalars .0 index */ +typedef struct mib_node mib_scalar_node; + +/** derived node, points to a fixed size const array + of sub-identifiers plus a 'child' pointer */ +struct mib_array_node +{ + /* inherited "base class" members */ + void (* const get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (* const get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + const u8_t node_type; + const u16_t maxlength; + + /* aditional struct members */ + const s32_t *objid; + struct mib_node* const *nptr; +}; + +/** derived node, points to a fixed size mem_malloced array + of sub-identifiers plus a 'child' pointer */ +struct mib_ram_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + s32_t *objid; + struct mib_node **nptr; +}; + +struct mib_list_node +{ + struct mib_list_node *prev; + struct mib_list_node *next; + s32_t objid; + struct mib_node *nptr; +}; + +/** derived node, points to a doubly linked list + of sub-identifiers plus a 'child' pointer */ +struct mib_list_rootnode +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + struct mib_list_node *head; + struct mib_list_node *tail; + /* counts list nodes in list */ + u16_t count; +}; + +/** derived node, has access functions for mib object in external memory or device + using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */ +struct mib_external_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + /** points to an extenal (in memory) record of some sort of addressing + information, passed to and interpreted by the funtions below */ + void* addr_inf; + /** tree levels under this node */ + u8_t tree_levels; + /** number of objects at this level */ + u16_t (*level_length)(void* addr_inf, u8_t level); + /** compares object sub identifier with external id + return zero when equal, nonzero when unequal */ + s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id); + void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id); + + /** async Questions */ + void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_q)(u8_t rid, struct obj_def *od); + void (*set_test_q)(u8_t rid, struct obj_def *od); + void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Answers */ + void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Panic Close (agent returns error reply, + e.g. used for external transaction cleanup) */ + void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_pc)(u8_t rid, struct obj_def *od); + void (*set_test_pc)(u8_t rid, struct obj_def *od); + void (*set_value_pc)(u8_t rid, struct obj_def *od); +}; + +/** export MIB tree from mib2.c */ +extern const struct mib_array_node internet; + +/** dummy function pointers for non-leaf MIB nodes from mib2.c */ +void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +void noleafs_get_value(struct obj_def *od, u16_t len, void *value); +u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value); +void noleafs_set_value(struct obj_def *od, u16_t len, void *value); + +void snmp_oidtoip(s32_t *ident, struct ip_addr *ip); +void snmp_iptooid(struct ip_addr *ip, s32_t *ident); +void snmp_ifindextonetif(s32_t ifindex, struct netif **netif); +void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx); + +struct mib_list_node* snmp_mib_ln_alloc(s32_t id); +void snmp_mib_ln_free(struct mib_list_node *ln); +struct mib_list_rootnode* snmp_mib_lrn_alloc(void); +void snmp_mib_lrn_free(struct mib_list_rootnode *lrn); + +s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn); +s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn); +struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n); + +struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np); +struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); +u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident); +u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_STRUCTS_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/sockets.h b/packages/net/lwip_tcpip/current/include/lwip/sockets.h --- a/packages/net/lwip_tcpip/current/include/lwip/sockets.h +++ b/packages/net/lwip_tcpip/current/include/lwip/sockets.h @@ -33,8 +33,21 @@ #ifndef __LWIP_SOCKETS_H__ #define __LWIP_SOCKETS_H__ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + #include "lwip/ip_addr.h" +#include "lwip/inet.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* members are in network byte order */ struct sockaddr_in { u8_t sin_len; u8_t sin_family; @@ -50,42 +63,43 @@ struct sockaddr { }; #ifndef socklen_t -# define socklen_t int +# define socklen_t u32_t #endif - +/* Socket protocol types (TCP/UDP/RAW) */ #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 /* - * Option flags per-socket. + * Option flags per-socket. These must match the SOF_ flags in ip.h! */ -#define SO_DEBUG 0x0001 /* turn on debugging info recording */ -#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ -#define SO_REUSEADDR 0x0004 /* allow local address reuse */ -#define SO_KEEPALIVE 0x0008 /* keep connections alive */ -#define SO_DONTROUTE 0x0010 /* just use interface addresses */ -#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */ -#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */ -#define SO_LINGER 0x0080 /* linger on close if data present */ -#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ -#define SO_REUSEPORT 0x0200 /* allow local address & port reuse */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* Unimplemented: allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ -#define SO_DONTLINGER (int)(~SO_LINGER) +#define SO_DONTLINGER ((int)(~SO_LINGER)) /* * Additional options, not kept in so_options. */ -#define SO_SNDBUF 0x1001 /* send buffer size */ -#define SO_RCVBUF 0x1002 /* receive buffer size */ -#define SO_SNDLOWAT 0x1003 /* send low-water mark */ -#define SO_RCVLOWAT 0x1004 /* receive low-water mark */ -#define SO_SNDTIMEO 0x1005 /* send timeout */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */ #define SO_RCVTIMEO 0x1006 /* receive timeout */ -#define SO_ERROR 0x1007 /* get error status and clear */ -#define SO_TYPE 0x1008 /* get socket type */ - +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ /* @@ -110,21 +124,74 @@ struct linger { #define IPPROTO_IP 0 #define IPPROTO_TCP 6 #define IPPROTO_UDP 17 - -#define INADDR_ANY 0 -#define INADDR_BROADCAST 0xffffffff +#define IPPROTO_UDPLITE 136 /* Flags we can use with send and recv. */ -#define MSG_DONTWAIT 0x40 /* Nonblocking i/o for this operation only */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ /* * Options for level IPPROTO_IP */ -#define IP_TOS 1 -#define IP_TTL 2 +#define IP_TOS 1 +#define IP_TTL 2 + +#if LWIP_TCP +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#endif /* LWIP_TCP */ + +#if LWIP_UDP && LWIP_UDPLITE +/* + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +#endif /* LWIP_UDP && LWIP_UDPLITE*/ +#if LWIP_IGMP +/* + * Options and types for UDP multicast traffic handling + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; +#endif /* LWIP_IGMP */ + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ #define IPTOS_TOS_MASK 0x1E #define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) #define IPTOS_LOWDELAY 0x10 @@ -134,7 +201,13 @@ struct linger { #define IPTOS_MINCOST IPTOS_LOWCOST /* - * Definitions for IP precedence (also in ip_tos) (hopefully unused) + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. */ #define IPTOS_PREC_MASK 0xe0 #define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) @@ -150,7 +223,7 @@ struct linger { /* * Commands for ioctlsocket(), taken from the BSD file fcntl.h. - * + * lwip_ioctl only supports FIONREAD and FIONBIO, for now * * Ioctl's have the command encoded in the lower word, * and the size of any in or out parameters in the upper @@ -159,10 +232,10 @@ struct linger { * we restrict parameters to at most 128 bytes. */ #if !defined(FIONREAD) || !defined(FIONBIO) -#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ -#define IOC_VOID 0x20000000 /* no parameters */ -#define IOC_OUT 0x40000000 /* copy out parameters */ -#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ #define IOC_INOUT (IOC_IN|IOC_OUT) /* 0x20000000 distinguishes new & old ioctl's */ @@ -171,7 +244,7 @@ struct linger { #define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) #define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) -#endif +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ #ifndef FIONREAD #define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ @@ -180,7 +253,7 @@ struct linger { #define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ #endif -/* Socket I/O Controls */ +/* Socket I/O Controls: unimplemented */ #ifndef SIOCSHIWAT #define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ #define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ @@ -189,13 +262,16 @@ struct linger { #define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ #endif +/* Socket flags: */ #ifndef O_NONBLOCK #define O_NONBLOCK 04000U #endif +/* FD_SET used for lwip_select */ #ifndef FD_SET #undef FD_SETSIZE - #define FD_SETSIZE 16 + /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */ + #define FD_SETSIZE MEMP_NUM_NETCONN #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7))) #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7))) #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7))) @@ -205,42 +281,42 @@ struct linger { unsigned char fd_bits [(FD_SETSIZE+7)/8]; } fd_set; -/* - * only define this in sockets.c so it does not interfere - * with other projects namespaces where timeval is present - */ +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ #ifndef LWIP_TIMEVAL_PRIVATE #define LWIP_TIMEVAL_PRIVATE 1 #endif #if LWIP_TIMEVAL_PRIVATE - struct timeval { - long tv_sec; /* seconds */ - long tv_usec; /* and microseconds */ - }; -#endif +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ -#endif +void lwip_socket_init(void); int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen); -int lwip_bind(int s, struct sockaddr *name, socklen_t namelen); +int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen); int lwip_shutdown(int s, int how); int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen); int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen); int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); int lwip_close(int s); -int lwip_connect(int s, struct sockaddr *name, socklen_t namelen); +int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen); int lwip_listen(int s, int backlog); -int lwip_recv(int s, void *mem, int len, unsigned int flags); -int lwip_read(int s, void *mem, int len); -int lwip_recvfrom(int s, void *mem, int len, unsigned int flags, +int lwip_recv(int s, void *mem, size_t len, int flags); +int lwip_read(int s, void *mem, size_t len); +int lwip_recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); -int lwip_send(int s, void *dataptr, int size, unsigned int flags); -int lwip_sendto(int s, void *dataptr, int size, unsigned int flags, - struct sockaddr *to, socklen_t tolen); +int lwip_send(int s, const void *dataptr, size_t size, int flags); +int lwip_sendto(int s, const void *dataptr, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); int lwip_socket(int domain, int type, int protocol); -int lwip_write(int s, void *dataptr, int size); +int lwip_write(int s, const void *dataptr, size_t size); int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); int lwip_ioctl(int s, long cmd, void *argp); @@ -249,7 +325,7 @@ int lwip_ioctl(int s, long cmd, void *ar #define accept(a,b,c) lwip_accept(a,b,c) #define bind(a,b,c) lwip_bind(a,b,c) #define shutdown(a,b) lwip_shutdown(a,b) -#define close(s) lwip_close(s) +#define closesocket(s) lwip_close(s) #define connect(a,b,c) lwip_connect(a,b,c) #define getsockname(a,b,c) lwip_getsockname(a,b,c) #define getpeername(a,b,c) lwip_getpeername(a,b,c) @@ -257,15 +333,25 @@ int lwip_ioctl(int s, long cmd, void *ar #define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e) #define listen(a,b) lwip_listen(a,b) #define recv(a,b,c,d) lwip_recv(a,b,c,d) -#define read(a,b,c) lwip_read(a,b,c) #define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f) #define send(a,b,c,d) lwip_send(a,b,c,d) #define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f) #define socket(a,b,c) lwip_socket(a,b,c) -#define write(a,b,c) lwip_write(a,b,c) #define select(a,b,c,d,e) lwip_select(a,b,c,d,e) #define ioctlsocket(a,b,c) lwip_ioctl(a,b,c) + +#if LWIP_POSIX_SOCKETS_IO_NAMES +#define read(a,b,c) lwip_read(a,b,c) +#define write(a,b,c) lwip_write(a,b,c) +#define close(s) lwip_close(s) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + #endif /* LWIP_COMPAT_SOCKETS */ +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + #endif /* __LWIP_SOCKETS_H__ */ - diff --git a/packages/net/lwip_tcpip/current/include/lwip/stats.h b/packages/net/lwip_tcpip/current/include/lwip/stats.h --- a/packages/net/lwip_tcpip/current/include/lwip/stats.h +++ b/packages/net/lwip_tcpip/current/include/lwip/stats.h @@ -33,50 +33,67 @@ #define __LWIP_STATS_H__ #include "lwip/opt.h" -#include "arch/cc.h" #include "lwip/mem.h" #include "lwip/memp.h" +#ifdef __cplusplus +extern "C" { +#endif + #if LWIP_STATS +#ifndef LWIP_STATS_LARGE +#define LWIP_STATS_LARGE 0 +#endif + +#if LWIP_STATS_LARGE +#define STAT_COUNTER u32_t +#define STAT_COUNTER_F U32_F +#else +#define STAT_COUNTER u16_t +#define STAT_COUNTER_F U16_F +#endif + struct stats_proto { - u16_t xmit; /* Transmitted packets. */ - u16_t rexmit; /* Retransmitted packets. */ - u16_t recv; /* Received packets. */ - u16_t fw; /* Forwarded packets. */ - u16_t drop; /* Dropped packets. */ - u16_t chkerr; /* Checksum error. */ - u16_t lenerr; /* Invalid length error. */ - u16_t memerr; /* Out of memory error. */ - u16_t rterr; /* Routing error. */ - u16_t proterr; /* Protocol error. */ - u16_t opterr; /* Error in options. */ - u16_t err; /* Misc error. */ - u16_t cachehit; + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER rterr; /* Routing error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER opterr; /* Error in options. */ + STAT_COUNTER err; /* Misc error. */ + STAT_COUNTER cachehit; +}; + +struct stats_igmp { + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER v1_rxed; /* */ + STAT_COUNTER join_sent; /* */ + STAT_COUNTER leave_sent; /* */ + STAT_COUNTER unicast_query; /* */ + STAT_COUNTER report_sent; /* */ + STAT_COUNTER report_rxed; /* */ + STAT_COUNTER group_query_rxed; /* */ }; struct stats_mem { mem_size_t avail; mem_size_t used; - mem_size_t max; - mem_size_t err; -}; - -struct stats_pbuf { - u16_t avail; - u16_t used; - u16_t max; - u16_t err; - - u16_t alloc_locked; - u16_t refresh_locked; + mem_size_t max; + STAT_COUNTER err; + STAT_COUNTER illegal; }; struct stats_syselem { - u16_t used; - u16_t max; - u16_t err; + STAT_COUNTER used; + STAT_COUNTER max; + STAT_COUNTER err; }; struct stats_sys { @@ -85,74 +102,182 @@ struct stats_sys { }; struct stats_ { +#if LINK_STATS struct stats_proto link; +#endif +#if ETHARP_STATS + struct stats_proto etharp; +#endif +#if IPFRAG_STATS struct stats_proto ip_frag; +#endif +#if IP_STATS struct stats_proto ip; +#endif +#if ICMP_STATS struct stats_proto icmp; +#endif +#if IGMP_STATS + struct stats_igmp igmp; +#endif +#if UDP_STATS struct stats_proto udp; +#endif +#if TCP_STATS struct stats_proto tcp; - struct stats_pbuf pbuf; +#endif +#if MEM_STATS struct stats_mem mem; +#endif +#if MEMP_STATS struct stats_mem memp[MEMP_MAX]; +#endif +#if SYS_STATS struct stats_sys sys; +#endif }; extern struct stats_ lwip_stats; - -void stats_init(void); +#define stats_init() /* Compatibility define, not init needed. */ #define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x #else #define stats_init() #define STATS_INC(x) +#define STATS_DEC(x) #endif /* LWIP_STATS */ #if TCP_STATS #define TCP_STATS_INC(x) STATS_INC(x) +#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") #else #define TCP_STATS_INC(x) +#define TCP_STATS_DISPLAY() #endif #if UDP_STATS #define UDP_STATS_INC(x) STATS_INC(x) +#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") #else #define UDP_STATS_INC(x) +#define UDP_STATS_DISPLAY() #endif #if ICMP_STATS #define ICMP_STATS_INC(x) STATS_INC(x) +#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") #else #define ICMP_STATS_INC(x) +#define ICMP_STATS_DISPLAY() +#endif + +#if IGMP_STATS +#define IGMP_STATS_INC(x) STATS_INC(x) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp) +#else +#define IGMP_STATS_INC(x) +#define IGMP_STATS_DISPLAY() #endif #if IP_STATS #define IP_STATS_INC(x) STATS_INC(x) +#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") #else #define IP_STATS_INC(x) +#define IP_STATS_DISPLAY() #endif #if IPFRAG_STATS #define IPFRAG_STATS_INC(x) STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") #else #define IPFRAG_STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() +#endif + +#if ETHARP_STATS +#define ETHARP_STATS_INC(x) STATS_INC(x) +#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") +#else +#define ETHARP_STATS_INC(x) +#define ETHARP_STATS_DISPLAY() #endif #if LINK_STATS #define LINK_STATS_INC(x) STATS_INC(x) +#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") #else #define LINK_STATS_INC(x) +#define LINK_STATS_DISPLAY() +#endif + +#if MEM_STATS +#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y +#define MEM_STATS_INC(x) STATS_INC(mem.x) +#define MEM_STATS_INC_USED(x, y) do { lwip_stats.mem.used += y; \ + if (lwip_stats.mem.max < lwip_stats.mem.used) { \ + lwip_stats.mem.max = lwip_stats.mem.used; \ + } \ + } while(0) +#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y +#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") +#else +#define MEM_STATS_AVAIL(x, y) +#define MEM_STATS_INC(x) +#define MEM_STATS_INC_USED(x, y) +#define MEM_STATS_DEC_USED(x, y) +#define MEM_STATS_DISPLAY() +#endif + +#if MEMP_STATS +#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y +#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x) +#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x) +#define MEMP_STATS_INC_USED(x, i) do { ++lwip_stats.memp[i].used; \ + if (lwip_stats.memp[i].max < lwip_stats.memp[i].used) { \ + lwip_stats.memp[i].max = lwip_stats.memp[i].used; \ + } \ + } while(0) +#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i) +#else +#define MEMP_STATS_AVAIL(x, i, y) +#define MEMP_STATS_INC(x, i) +#define MEMP_STATS_DEC(x, i) +#define MEMP_STATS_INC_USED(x, i) +#define MEMP_STATS_DISPLAY(i) +#endif + +#if SYS_STATS +#define SYS_STATS_INC(x) STATS_INC(sys.x) +#define SYS_STATS_DEC(x) STATS_DEC(sys.x) +#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_DISPLAY() #endif /* Display of statistics */ #if LWIP_STATS_DISPLAY void stats_display(void); +void stats_display_proto(struct stats_proto *proto, char *name); +void stats_display_igmp(struct stats_igmp *igmp); +void stats_display_mem(struct stats_mem *mem, char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); #else #define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp) +#define stats_display_mem(mem, name) +#define stats_display_memp(mem, index) +#define stats_display_sys(sys) +#endif /* LWIP_STATS_DISPLAY */ + +#ifdef __cplusplus +} #endif #endif /* __LWIP_STATS_H__ */ - - - - diff --git a/packages/net/lwip_tcpip/current/include/lwip/sys.h b/packages/net/lwip_tcpip/current/include/lwip/sys.h --- a/packages/net/lwip_tcpip/current/include/lwip/sys.h +++ b/packages/net/lwip_tcpip/current/include/lwip/sys.h @@ -32,10 +32,11 @@ #ifndef __LWIP_SYS_H__ #define __LWIP_SYS_H__ -#include "arch/cc.h" - #include "lwip/opt.h" +#ifdef __cplusplus +extern "C" { +#endif #if NO_SYS @@ -43,7 +44,7 @@ definitions of the sys_ functions. */ typedef u8_t sys_sem_t; typedef u8_t sys_mbox_t; -struct sys_timeout {u8_t dummy;}; +struct sys_timeo {u8_t dummy;}; #define sys_init() #define sys_timeout(m,h,a) @@ -51,32 +52,42 @@ struct sys_timeout {u8_t dummy;}; #define sys_sem_new(c) c #define sys_sem_signal(s) #define sys_sem_wait(s) +#define sys_sem_wait_timeout(s,t) +#define sys_arch_sem_wait(s,t) #define sys_sem_free(s) -#define sys_mbox_new() 0 +#define sys_mbox_new(s) 0 #define sys_mbox_fetch(m,d) +#define sys_mbox_tryfetch(m,d) #define sys_mbox_post(m,d) +#define sys_mbox_trypost(m,d) #define sys_mbox_free(m) -#define sys_thread_new(t,a,p) +#define sys_thread_new(n,t,a,s,p) #else /* NO_SYS */ -#include "arch/sys_arch.h" +/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL -/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ -#define SYS_ARCH_TIMEOUT 0xffffffff +/* sys_mbox_tryfetch returns SYS_MBOX_EMPTY if appropriate. + * For now we use the same magic value, but we allow this to change in future. + */ +#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT + +#include "lwip/err.h" +#include "arch/sys_arch.h" typedef void (* sys_timeout_handler)(void *arg); -struct sys_timeout { - struct sys_timeout *next; +struct sys_timeo { + struct sys_timeo *next; u32_t time; sys_timeout_handler h; void *arg; }; struct sys_timeouts { - struct sys_timeout *next; + struct sys_timeo *next; }; /* sys_init() must be called before anthing else. */ @@ -112,23 +123,26 @@ u32_t sys_jiffies(void); /* since power #endif /* Mailbox functions. */ -sys_mbox_t sys_mbox_new(void); +sys_mbox_t sys_mbox_new(int size); void sys_mbox_post(sys_mbox_t mbox, void *msg); +err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg); u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout); +#ifndef sys_arch_mbox_tryfetch /* Allow port to override with a macro */ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg); +#endif +/* For now, we map straight to sys_arch implementation. */ +#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg) void sys_mbox_free(sys_mbox_t mbox); void sys_mbox_fetch(sys_mbox_t mbox, void **msg); - /* Thread functions. */ -sys_thread_t sys_thread_new(void (* thread)(void *arg), void *arg, int prio); - -/* The following functions are used only in Unix code, and - can be omitted when porting the stack. */ -/* Returns the current time in microseconds. */ -unsigned long sys_now(void); +sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio); #endif /* NO_SYS */ +/** Returns the current time in milliseconds. */ +u32_t sys_now(void); + /* Critical Region Protection */ /* These functions must be implemented in the sys_arch.c file. In some implementations they can provide a more light-weight protection @@ -180,4 +194,50 @@ void sys_arch_unprotect(sys_prot_t pval) #endif /* SYS_ARCH_PROTECT */ +/* + * Macros to set/get and increase/decrease variables in a thread-safe way. + * Use these for accessing variable that are used from more than one thread. + */ + +#ifndef SYS_ARCH_INC +#define SYS_ARCH_INC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var += val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_INC */ + +#ifndef SYS_ARCH_DEC +#define SYS_ARCH_DEC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var -= val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_DEC */ + +#ifndef SYS_ARCH_GET +#define SYS_ARCH_GET(var, ret) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + ret = var; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_GET */ + +#ifndef SYS_ARCH_SET +#define SYS_ARCH_SET(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var = val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_SET */ + + +#ifdef __cplusplus +} +#endif + #endif /* __LWIP_SYS_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/tcp.h b/packages/net/lwip_tcpip/current/include/lwip/tcp.h --- a/packages/net/lwip_tcpip/current/include/lwip/tcp.h +++ b/packages/net/lwip_tcpip/current/include/lwip/tcp.h @@ -32,61 +32,83 @@ #ifndef __LWIP_TCP_H__ #define __LWIP_TCP_H__ +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + #include "lwip/sys.h" #include "lwip/mem.h" - #include "lwip/pbuf.h" -#include "lwip/opt.h" #include "lwip/ip.h" #include "lwip/icmp.h" +#include "lwip/err.h" -#include "lwip/err.h" +#ifdef __cplusplus +extern "C" { +#endif struct tcp_pcb; /* Functions for interfacing with TCP: */ /* Lower layer interface to TCP: */ -void tcp_init (void); /* Must be called first to - initialize TCP. */ +#define tcp_init() /* Compatibility define, not init needed. */ void tcp_tmr (void); /* Must be called every - TCP_TMR_INTERVAL - ms. (Typically 250 ms). */ + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ /* Application program's interface: */ struct tcp_pcb * tcp_new (void); struct tcp_pcb * tcp_alloc (u8_t prio); void tcp_arg (struct tcp_pcb *pcb, void *arg); void tcp_accept (struct tcp_pcb *pcb, - err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err)); void tcp_recv (struct tcp_pcb *pcb, - err_t (* recv)(void *arg, struct tcp_pcb *tpcb, - struct pbuf *p, err_t err)); + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)); void tcp_sent (struct tcp_pcb *pcb, - err_t (* sent)(void *arg, struct tcp_pcb *tpcb, - u16_t len)); + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)); void tcp_poll (struct tcp_pcb *pcb, - err_t (* poll)(void *arg, struct tcp_pcb *tpcb), - u8_t interval); + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), + u8_t interval); void tcp_err (struct tcp_pcb *pcb, - void (* err)(void *arg, err_t err)); + void (* err)(void *arg, err_t err)); #define tcp_mss(pcb) ((pcb)->mss) #define tcp_sndbuf(pcb) ((pcb)->snd_buf) +#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY) +#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY) +#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0) + +#if TCP_LISTEN_BACKLOG +#define tcp_accepted(pcb) (((struct tcp_pcb_listen *)(pcb))->accepts_pending--) +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) +#endif /* TCP_LISTEN_BACKLOG */ void tcp_recved (struct tcp_pcb *pcb, u16_t len); err_t tcp_bind (struct tcp_pcb *pcb, struct ip_addr *ipaddr, - u16_t port); + u16_t port); err_t tcp_connect (struct tcp_pcb *pcb, struct ip_addr *ipaddr, - u16_t port, err_t (* connected)(void *arg, - struct tcp_pcb *tpcb, - err_t err)); -struct tcp_pcb * tcp_listen (struct tcp_pcb *pcb); -void tcp_abort (struct tcp_pcb *pcb); + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abandon (struct tcp_pcb *pcb, int reset); +#define tcp_abort(pcb) tcp_abandon((pcb), 1) err_t tcp_close (struct tcp_pcb *pcb); + +/* Flags for "apiflags" parameter in tcp_write and tcp_enqueue */ +#define TCP_WRITE_FLAG_COPY 0x01 +#define TCP_WRITE_FLAG_MORE 0x02 + err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len, - u8_t copy); + u8_t apiflags); void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); @@ -103,10 +125,28 @@ void tcp_fasttmr (void); /* Only used by IP to pass a TCP segment to TCP: */ void tcp_input (struct pbuf *p, struct netif *inp); /* Used within the TCP code only: */ +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); err_t tcp_output (struct tcp_pcb *pcb); void tcp_rexmit (struct tcp_pcb *pcb); void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +/** + * This is the Nagle algorithm: try to combine user data to send as few TCP + * segments as possible. Only send if + * - no previously transmitted data on the connection remains unacknowledged or + * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or + * - the only unsent segment is at least pcb->mss bytes long (or there is more + * than one unsent segment - with lwIP, this can happen although unsent->len < mss) + * - or if we are in fast-retransmit (TF_INFR) + */ +#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ + ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ + (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ + ((tpcb)->unsent->len >= (tpcb)->mss))) \ + ) ? 1 : 0) +#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) #define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0) @@ -133,40 +173,44 @@ void tcp_rexmit_rto (struct #define TCP_HLEN 20 #ifndef TCP_TMR_INTERVAL -#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in - milliseconds. */ +#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */ #endif /* TCP_TMR_INTERVAL */ #ifndef TCP_FAST_INTERVAL -#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in - milliseconds */ +#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */ #endif /* TCP_FAST_INTERVAL */ #ifndef TCP_SLOW_INTERVAL -#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in - milliseconds */ +#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */ #endif /* TCP_SLOW_INTERVAL */ #define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ #define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ -#define TCP_OOSEQ_TIMEOUT 6 /* x RTO */ +#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */ + +#ifndef TCP_MSL +#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */ +#endif + +/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ +#ifndef TCP_KEEPIDLE_DEFAULT +#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif -#define TCP_MSL 60000 /* The maximum segment lifetime in microseconds */ +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif -/* - * User-settable options (used with setsockopt). +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */ +#endif + +#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ + +/* Fields are (of course) in network byte order. + * Some fields are converted to host byte order in tcp_input(). */ -#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ -#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keepalive miliseconds */ - -/* Keepalive values */ -#define TCP_KEEPDEFAULT 7200000 /* KEEPALIVE timer in miliseconds */ -#define TCP_KEEPINTVL 75000 /* Time between KEEPALIVE probes in miliseconds */ -#define TCP_KEEPCNT 9 /* Counter for KEEPALIVE probes */ -#define TCP_MAXIDLE TCP_KEEPCNT * TCP_KEEPINTVL /* Maximum KEEPALIVE probe time */ - - #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif @@ -192,12 +236,11 @@ PACK_STRUCT_END #define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr)) #define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) -#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons((ntohs((phdr)->_hdrlen_rsvd_flags) & ~TCP_FLAGS) | (flags)) -#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (flags)) +#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & htons((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags)) +#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags)) #define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) ) -#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & TCP_FIN || \ - TCPH_FLAGS((seg)->tcphdr) & TCP_SYN)? 1: 0)) +#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0)) enum tcp_state { CLOSED = 0, @@ -213,38 +256,73 @@ enum tcp_state { TIME_WAIT = 10 }; +/** Flags used on input processing, not on pcb->flags +*/ +#define TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ +#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ + + +#if LWIP_CALLBACK_API + /* Function to call when a listener has been connected. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb a new tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: accept the new connection, + * any other err_t abortsthe new connection + */ +#define DEF_ACCEPT_CALLBACK err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err) +#else /* LWIP_CALLBACK_API */ +#define DEF_ACCEPT_CALLBACK +#endif /* LWIP_CALLBACK_API */ + +/** + * members common to struct tcp_pcb and struct tcp_listen_pcb + */ +#define TCP_PCB_COMMON(type) \ + type *next; /* for the linked list */ \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + void *callback_arg; \ + /* ports are in host byte order */ \ + u16_t local_port; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK + + /* the TCP protocol control block */ struct tcp_pcb { /** common PCB members */ IP_PCB; /** protocol specific PCB members */ - struct tcp_pcb *next; /* for the linked list */ - enum tcp_state state; /* TCP state */ - u8_t prio; - void *callback_arg; + TCP_PCB_COMMON(struct tcp_pcb); - u16_t local_port; + /* ports are in host byte order */ u16_t remote_port; u8_t flags; -#define TF_ACK_DELAY (u8_t)0x01U /* Delayed ACK. */ -#define TF_ACK_NOW (u8_t)0x02U /* Immediate ACK. */ -#define TF_INFR (u8_t)0x04U /* In fast recovery. */ -#define TF_RESET (u8_t)0x08U /* Connection was reset. */ -#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ -#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ -#define TF_NODELAY (u8_t)0x40U /* Disable Nagle algorithm */ +#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */ +#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */ +#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */ +#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */ +#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ + /* the rest of the fields are in host byte order + as we have to do some math with them */ /* receiver variables */ u32_t rcv_nxt; /* next seqno expected */ - u16_t rcv_wnd; /* receiver window */ - + u16_t rcv_wnd; /* receiver window available */ + u16_t rcv_ann_wnd; /* receiver window to announce */ + u32_t rcv_ann_right_edge; /* announced right edge of window */ + /* Timers */ u32_t tmr; u8_t polltmr, pollinterval; /* Retransmission timer. */ - u16_t rtime; + s16_t rtime; u16_t mss; /* maximum segment size */ @@ -253,7 +331,7 @@ struct tcp_pcb { u32_t rtseq; /* sequence number being timed */ s16_t sa, sv; /* @todo document this */ - u16_t rto; /* retransmission time-out */ + s16_t rto; /* retransmission time-out */ u8_t nrtx; /* number of retransmissions */ /* fast retransmit/recovery */ @@ -265,17 +343,17 @@ struct tcp_pcb { u16_t ssthresh; /* sender variables */ - u32_t snd_nxt, /* next seqno to be sent */ - snd_max, /* Highest seqno sent. */ - snd_wnd, /* sender window */ - snd_wl1, snd_wl2, /* Sequence and acknowledgement numbers of last - window update. */ - snd_lbb; /* Sequence number of next byte to be buffered. */ + u32_t snd_nxt; /* next new seqno to be sent */ + u16_t snd_wnd; /* sender window */ + u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last + window update. */ + u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ u16_t acked; u16_t snd_buf; /* Available buffer space for sending (in bytes). */ - u8_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffff-3) + u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ /* These are ordered by sequence number: */ @@ -285,55 +363,85 @@ struct tcp_pcb { struct tcp_seg *ooseq; /* Received out of sequence segments. */ #endif /* TCP_QUEUE_OOSEQ */ + struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ + #if LWIP_CALLBACK_API - /* Function to be called when more send buffer space is available. */ + /* Function to be called when more send buffer space is available. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb which has send buffer space available + * @param space the amount of bytes available + * @return ERR_OK: try to send some data by calling tcp_output + */ err_t (* sent)(void *arg, struct tcp_pcb *pcb, u16_t space); - /* Function to be called when (in-sequence) data has arrived. */ + /* Function to be called when (in-sequence) data has arrived. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb for which data has arrived + * @param p the packet buffer which arrived + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: try to send some data by calling tcp_output + */ err_t (* recv)(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); - /* Function to be called when a connection has been set up. */ + /* Function to be called when a connection has been set up. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return value is currently ignored + */ err_t (* connected)(void *arg, struct tcp_pcb *pcb, err_t err); - /* Function to call when a listener has been connected. */ - err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err); - - /* Function which is called periodically. */ + /* Function which is called periodically. + * The period can be adjusted in multiples of the TCP slow timer interval + * by changing tcp_pcb.polltmr. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb to poll for + * @return ERR_OK: try to send some data by calling tcp_output + */ err_t (* poll)(void *arg, struct tcp_pcb *pcb); - /* Function to be called whenever a fatal error occurs. */ + /* Function to be called whenever a fatal error occurs. + * There is no pcb parameter since most of the times, the pcb is + * already deallocated (or there is no pcb) when this function is called. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param err an indication why the error callback is called: + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ void (* errf)(void *arg, err_t err); #endif /* LWIP_CALLBACK_API */ +#if LWIP_TCP_TIMESTAMPS + u32_t ts_lastacksent; + u32_t ts_recent; +#endif /* LWIP_TCP_TIMESTAMPS */ + /* idle time before KEEPALIVE is sent */ - u32_t keepalive; + u32_t keep_idle; +#if LWIP_TCP_KEEPALIVE + u32_t keep_intvl; + u32_t keep_cnt; +#endif /* LWIP_TCP_KEEPALIVE */ + /* Persist timer counter */ + u32_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + /* KEEPALIVE counter */ - u8_t keep_cnt; + u8_t keep_cnt_sent; }; struct tcp_pcb_listen { /* Common members of all PCB types */ IP_PCB; - /* Protocol specific PCB members */ - struct tcp_pcb_listen *next; /* for the linked list */ - - /* Even if state is obviously LISTEN this is here for - * field compatibility with tpc_pcb to which it is cast sometimes - * Until a cleaner solution emerges this is here.FIXME - */ - enum tcp_state state; /* TCP state */ + TCP_PCB_COMMON(struct tcp_pcb_listen); - u8_t prio; - void *callback_arg; - - u16_t local_port; - -#if LWIP_CALLBACK_API - /* Function to call when a listener has been connected. */ - err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err); -#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + u8_t backlog; + u8_t accepts_pending; +#endif /* TCP_LISTEN_BACKLOG */ }; #if LWIP_EVENT_API @@ -366,25 +474,50 @@ err_t lwip_tcp_event(void *arg, struct t #define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \ LWIP_EVENT_ERR, NULL, 0, (err)) #else /* LWIP_EVENT_API */ -#define TCP_EVENT_ACCEPT(pcb,err,ret) \ - if((pcb)->accept != NULL) \ - (ret = (pcb)->accept((pcb)->callback_arg,(pcb),(err))) -#define TCP_EVENT_SENT(pcb,space,ret) \ - if((pcb)->sent != NULL) \ - (ret = (pcb)->sent((pcb)->callback_arg,(pcb),(space))) -#define TCP_EVENT_RECV(pcb,p,err,ret) \ - if((pcb)->recv != NULL) \ - { ret = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err)); } else { \ - if (p) pbuf_free(p); } -#define TCP_EVENT_CONNECTED(pcb,err,ret) \ - if((pcb)->connected != NULL) \ - (ret = (pcb)->connected((pcb)->callback_arg,(pcb),(err))) -#define TCP_EVENT_POLL(pcb,ret) \ - if((pcb)->poll != NULL) \ - (ret = (pcb)->poll((pcb)->callback_arg,(pcb))) -#define TCP_EVENT_ERR(errf,arg,err) \ - if((errf) != NULL) \ - (errf)((arg),(err)) + +#define TCP_EVENT_ACCEPT(pcb,err,ret) \ + do { \ + if((pcb)->accept != NULL) \ + (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_SENT(pcb,space,ret) \ + do { \ + if((pcb)->sent != NULL) \ + (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_RECV(pcb,p,err,ret) \ + do { \ + if((pcb)->recv != NULL) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err)); \ + } else { \ + (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \ + } \ + } while (0) + +#define TCP_EVENT_CONNECTED(pcb,err,ret) \ + do { \ + if((pcb)->connected != NULL) \ + (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_POLL(pcb,ret) \ + do { \ + if((pcb)->poll != NULL) \ + (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_ERR(errf,arg,err) \ + do { \ + if((errf) != NULL) \ + (errf)((arg),(err)); \ + } while (0) + #endif /* LWIP_EVENT_API */ /* This structure represents a TCP segment on the unsent and unacked queues */ @@ -393,9 +526,22 @@ struct tcp_seg { struct pbuf *p; /* buffer containing data + TCP header */ void *dataptr; /* pointer to the TCP data in the pbuf */ u16_t len; /* the TCP length of this segment */ + u8_t flags; +#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ +#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ struct tcp_hdr *tcphdr; /* the TCP header */ }; +#define LWIP_TCP_OPT_LENGTH(flags) \ + (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \ + (flags & TF_SEG_OPTS_TS ? 12 : 0) + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION(x) (x) = htonl(((u32_t)2 << 24) | \ + ((u32_t)4 << 16) | \ + (((u32_t)TCP_MSS / 256) << 8) | \ + (TCP_MSS & 255)) + /* Internal functions and global variables: */ struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); void tcp_pcb_purge(struct tcp_pcb *pcb); @@ -405,21 +551,27 @@ u8_t tcp_segs_free(struct tcp_seg *seg); u8_t tcp_seg_free(struct tcp_seg *seg); struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); -#define tcp_ack(pcb) if((pcb)->flags & TF_ACK_DELAY) { \ - (pcb)->flags &= ~TF_ACK_DELAY; \ - (pcb)->flags |= TF_ACK_NOW; \ - tcp_output(pcb); \ - } else { \ - (pcb)->flags |= TF_ACK_DELAY; \ - } +#define tcp_ack(pcb) \ + do { \ + if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + tcp_output(pcb); \ + } \ + else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } \ + } while (0) -#define tcp_ack_now(pcb) (pcb)->flags |= TF_ACK_NOW; \ - tcp_output(pcb) +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + tcp_output(pcb); \ + } while (0) err_t tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags); err_t tcp_enqueue(struct tcp_pcb *pcb, void *dataptr, u16_t len, - u8_t flags, u8_t copy, - u8_t *optdata, u8_t optlen); + u8_t flags, u8_t apiflags, u8_t optflags); void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); @@ -430,10 +582,20 @@ void tcp_rst(u32_t seqno, u32_t ackno, u32_t tcp_next_iss(void); void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if LWIP_CALLBACK_API +err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +#endif /* LWIP_CALLBACK_API */ extern struct tcp_pcb *tcp_input_pcb; extern u32_t tcp_ticks; +const char* tcp_debug_state_str(enum tcp_state s); #if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG void tcp_debug_print(struct tcp_hdr *tcphdr); void tcp_debug_print_flags(u8_t flags); @@ -456,8 +618,8 @@ void tcp_timer_needed(void); /* The TCP PCB lists. */ union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ - struct tcp_pcb_listen *listen_pcbs; - struct tcp_pcb *pcbs; + struct tcp_pcb_listen *listen_pcbs; + struct tcp_pcb *pcbs; }; extern union tcp_listen_pcbs_t tcp_listen_pcbs; extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a @@ -497,7 +659,7 @@ extern struct tcp_pcb *tcp_tmp_pcb; if(*pcbs == npcb) { \ *pcbs = (*pcbs)->next; \ } else for(tcp_tmp_pcb = *pcbs; tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ - if(tcp_tmp_pcb->next != NULL && tcp_tmp_pcb->next == npcb) { \ + if(tcp_tmp_pcb->next == npcb) { \ tcp_tmp_pcb->next = npcb->next; \ break; \ } \ @@ -508,24 +670,38 @@ extern struct tcp_pcb *tcp_tmp_pcb; } while(0) #else /* LWIP_DEBUG */ -#define TCP_REG(pcbs, npcb) do { \ - npcb->next = *pcbs; \ - *(pcbs) = npcb; \ - tcp_timer_needed(); \ - } while(0) -#define TCP_RMV(pcbs, npcb) do { \ - if(*(pcbs) == npcb) { \ - (*(pcbs)) = (*pcbs)->next; \ - } else for(tcp_tmp_pcb = *pcbs; tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ - if(tcp_tmp_pcb->next != NULL && tcp_tmp_pcb->next == npcb) { \ - tcp_tmp_pcb->next = npcb->next; \ - break; \ - } \ - } \ - npcb->next = NULL; \ - } while(0) + +#define TCP_REG(pcbs, npcb) \ + do { \ + npcb->next = *pcbs; \ + *(pcbs) = npcb; \ + tcp_timer_needed(); \ + } while (0) + +#define TCP_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == npcb) { \ + (*(pcbs)) = (*pcbs)->next; \ + } \ + else { \ + for(tcp_tmp_pcb = *pcbs; \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == npcb) { \ + tcp_tmp_pcb->next = npcb->next; \ + break; \ + } \ + } \ + } \ + npcb->next = NULL; \ + } while(0) + #endif /* LWIP_DEBUG */ -#endif /* __LWIP_TCP_H__ */ +#ifdef __cplusplus +} +#endif +#endif /* LWIP_TCP */ +#endif /* __LWIP_TCP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/tcpip.h b/packages/net/lwip_tcpip/current/include/lwip/tcpip.h --- a/packages/net/lwip_tcpip/current/include/lwip/tcpip.h +++ b/packages/net/lwip_tcpip/current/include/lwip/tcpip.h @@ -32,27 +32,90 @@ #ifndef __LWIP_TCPIP_H__ #define __LWIP_TCPIP_H__ +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + #include "lwip/api_msg.h" +#include "lwip/netifapi.h" #include "lwip/pbuf.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_sem_t lock_tcpip_core; +#define LOCK_TCPIP_CORE() sys_sem_wait(lock_tcpip_core) +#define UNLOCK_TCPIP_CORE() sys_sem_signal(lock_tcpip_core) +#define TCPIP_APIMSG(m) tcpip_apimsg_lock(m) +#define TCPIP_APIMSG_ACK(m) +#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) +#define TCPIP_NETIFAPI_ACK(m) +#else +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#define TCPIP_APIMSG(m) tcpip_apimsg(m) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(m->conn->op_completed) +#define TCPIP_NETIFAPI(m) tcpip_netifapi(m) +#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(m->sem) +#endif /* LWIP_TCPIP_CORE_LOCKING */ void tcpip_init(void (* tcpip_init_done)(void *), void *arg); -void tcpip_apimsg(struct api_msg *apimsg); + +#if LWIP_NETCONN +err_t tcpip_apimsg(struct api_msg *apimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_apimsg_lock(struct api_msg *apimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETCONN */ + err_t tcpip_input(struct pbuf *p, struct netif *inp); -err_t tcpip_callback(void (*f)(void *ctx), void *ctx); + +#if LWIP_NETIF_API +err_t tcpip_netifapi(struct netifapi_msg *netifapimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ -void tcpip_tcp_timer_needed(void); +err_t tcpip_callback_with_block(void (*f)(void *ctx), void *ctx, u8_t block); +#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) + +/* free pbufs or heap memory from another context without blocking */ +err_t pbuf_free_callback(struct pbuf *p); +err_t mem_free_callback(void *m); + +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +err_t tcpip_untimeout(sys_timeout_handler h, void *arg); enum tcpip_msg_type { +#if LWIP_NETCONN TCPIP_MSG_API, - TCPIP_MSG_INPUT, - TCPIP_MSG_CALLBACK +#endif /* LWIP_NETCONN */ + TCPIP_MSG_INPKT, +#if LWIP_NETIF_API + TCPIP_MSG_NETIFAPI, +#endif /* LWIP_NETIF_API */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT }; struct tcpip_msg { enum tcpip_msg_type type; sys_sem_t *sem; union { +#if LWIP_NETCONN struct api_msg *apimsg; +#endif /* LWIP_NETCONN */ +#if LWIP_NETIF_API + struct netifapi_msg *netifapimsg; +#endif /* LWIP_NETIF_API */ struct { struct pbuf *p; struct netif *netif; @@ -61,8 +124,18 @@ struct tcpip_msg { void (*f)(void *ctx); void *ctx; } cb; + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; } msg; }; +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ #endif /* __LWIP_TCPIP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwip/udp.h b/packages/net/lwip_tcpip/current/include/lwip/udp.h --- a/packages/net/lwip_tcpip/current/include/lwip/udp.h +++ b/packages/net/lwip_tcpip/current/include/lwip/udp.h @@ -32,20 +32,36 @@ #ifndef __LWIP_UDP_H__ #define __LWIP_UDP_H__ -#include "lwip/arch.h" +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ #include "lwip/pbuf.h" -#include "lwip/inet.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" #include "lwip/ip.h" +#ifdef __cplusplus +extern "C" { +#endif + #define UDP_HLEN 8 +/* Fields are (of course) in network byte order. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN struct udp_hdr { PACK_STRUCT_FIELD(u16_t src); PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */ PACK_STRUCT_FIELD(u16_t len); PACK_STRUCT_FIELD(u16_t chksum); } PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif #define UDP_FLAGS_NOCHKSUM 0x01U #define UDP_FLAGS_UDPLITE 0x02U @@ -60,14 +76,40 @@ struct udp_pcb { struct udp_pcb *next; u8_t flags; + /* ports are in host byte order */ u16_t local_port, remote_port; - - u16_t chksum_len; - + +#if LWIP_IGMP + /* outgoing network interface for multicast packets */ + struct ip_addr multicast_ip; +#endif /* LWIP_IGMP */ + +#if LWIP_UDPLITE + /* used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; +#endif /* LWIP_UDPLITE */ + + /* receive callback function + * addr and port are in same byte order as in the pcb + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf + * makes 'addr' invalid, too. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + */ void (* recv)(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); + /* user-supplied argument for the recv callback */ void *recv_arg; }; +/* udp_pcbs export for exernal reference (e.g. SNMP agent) */ +extern struct udp_pcb *udp_pcbs; /* The following functions is the application layer interface to the UDP code. */ @@ -84,6 +126,7 @@ void udp_recv (struct struct ip_addr *addr, u16_t port), void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif); err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port); err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); @@ -92,13 +135,19 @@ err_t udp_send (struct /* The following functions are the lower layer interface to UDP. */ void udp_input (struct pbuf *p, struct netif *inp); -void udp_init (void); + +#define udp_init() /* Compatibility define, not init needed. */ #if UDP_DEBUG void udp_debug_print(struct udp_hdr *udphdr); #else #define udp_debug_print(udphdr) #endif -#endif /* __LWIP_UDP_H__ */ +#ifdef __cplusplus +} +#endif +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/lwipopts.h b/packages/net/lwip_tcpip/current/include/lwipopts.h --- a/packages/net/lwip_tcpip/current/include/lwipopts.h +++ b/packages/net/lwip_tcpip/current/include/lwipopts.h @@ -1,230 +1,545 @@ -/* - * 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: Jani Monoses - * - */ -#ifndef __LWIPOPTS_H__ -#define __LWIPOPTS_H__ -/*include the configuration made with configtool*/ +#ifndef CYGONCE_LWIPOPTS_H +#define CYGONCE_LWIPOPTS_H +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== + #include -/* ---------- Memory options ---------- */ -/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which - lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 - byte alignment -> define MEM_ALIGNMENT to 2. */ -#define MEM_ALIGNMENT CYGNUM_LWIP_MEM_ALIGNMENT + +#include + +//------------------------------------------------------------------------------ +// Platform specific locking +//------------------------------------------------------------------------------ + +#define SYS_LIGHTWEIGHT_PROT 1 +#define NO_SYS defined(CYGFUN_LWIP_MODE_SIMPLE) + +//------------------------------------------------------------------------------ +// Memory options +//------------------------------------------------------------------------------ + +#define MEM_LIBC_MALLOC defined(CYGIMP_LWIP_MEM_LIBC_MALLOC) +#define MEMP_MEM_MALLOC defined(CYGIMP_LWIP_MEMP_MEM_MALLOC) +#define MEM_ALIGNMENT CYGNUM_LWIP_MEM_ALIGNMENT +#define MEM_SIZE CYGNUM_LWIP_MEM_SIZE +#define MEMP_OVERFLOW_CHECK defined(CYGDBG_LWIP_MEMP_OVERFLOW_CHECK) +#define MEMP_SANITY_CHECK defined(CYGDBG_LWIP_MEMP_SANITY_CHECK) + +//#define MEM_USE_POOLS // not configurable +//#define MEM_USE_POOLS_TRY_BIGGER_POOL // not configurable +//#define MEMP_USE_CUSTOM_POOLS // not configurable +//#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT // not configurable + +//------------------------------------------------------------------------------ +// Internal memory pool sizes +//------------------------------------------------------------------------------ -/* MEM_SIZE: the size of the heap memory. If the application will send -a lot of data that needs to be copied, this should be set high. */ -#define MEM_SIZE CYGNUM_LWIP_MEM_SIZE +#define MEMP_NUM_PBUF CYGNUM_LWIP_MEMP_NUM_PBUF +#ifdef CYGPKG_LWIP_TCP +# define MEMP_NUM_TCP_PCB CYGNUM_LWIP_MEMP_NUM_TCP_PCB +# define MEMP_NUM_TCP_PCB_LISTEN CYGNUM_LWIP_MEMP_NUM_TCP_PCB_LISTEN +# define MEMP_NUM_TCP_SEG CYGNUM_LWIP_MEMP_NUM_TCP_SEG +#endif +#define MEMP_NUM_REASSDATA CYGNUM_LWIP_MEMP_NUM_REASSDATA +#ifdef CYGFUN_LWIP_ARP_QUEUEING +# define MEMP_NUM_ARP_QUEUE CYGNUM_LWIP_MEMP_NUM_ARP_QUEUE +#endif +#ifdef CYGPKG_LWIP_IGMP +# define MEMP_NUM_IGMP_GROUP CYGNUM_LWIP_MEMP_NUM_IGMP_GROUP +#endif +#define MEMP_NUM_SYS_TIMEOUT (CYGNUM_LWIP_MEMP_NUM_CORE_SYS_TIMEOUT + \ + CYGNUM_LWIP_MEMP_NUM_USER_SYS_TIMEOUT) +#define MEMP_NUM_NETBUF CYGNUM_LWIP_MEMP_NUM_NETBUF +#define MEMP_NUM_NETCONN CYGNUM_LWIP_MEMP_NUM_NETCONN +#define MEMP_NUM_TCPIP_MSG_API CYGNUM_LWIP_MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_INPKT CYGNUM_LWIP_MEMP_NUM_TCPIP_MSG_INPKT +#define PBUF_POOL_SIZE CYGNUM_LWIP_PBUF_POOL_SIZE + +//------------------------------------------------------------------------------ +// ARP options +//------------------------------------------------------------------------------ + +#ifdef CYGPKG_LWIP_ARP +# define LWIP_ARP 1 +# define ARP_TABLE_SIZE CYGNUM_LWIP_ARP_TABLE_SIZE +# define ARP_QUEUEING defined(CYGFUN_LWIP_ARP_QUEUEING) +# define ETHARP_TRUST_IP_MAC defined(CYGFUN_LWIP_ETHARP_TRUST_IP_MAC) +#endif -/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application - sends a lot of data out of ROM (or other static memory), this - should be set high. */ -#define MEMP_NUM_PBUF CYGNUM_LWIP_MEMP_NUM_PBUF -/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One - per active UDP "connection". */ -#define MEMP_NUM_UDP_PCB CYGNUM_LWIP_MEMP_NUM_UDP_PCB -/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP - connections. */ -#define MEMP_NUM_TCP_PCB CYGNUM_LWIP_MEMP_NUM_TCP_PCB -/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP - connections. */ -#define MEMP_NUM_TCP_PCB_LISTEN CYGNUM_LWIP_MEMP_NUM_TCP_PCB_LISTEN -/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP - segments. */ -#define MEMP_NUM_TCP_SEG CYGNUM_LWIP_MEMP_NUM_TCP_SEG -/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active - timeouts. */ -#define MEMP_NUM_SYS_TIMEOUT CYGNUM_LWIP_MEMP_NUM_SYS_TIMEOUT +//------------------------------------------------------------------------------ +// IP options +//------------------------------------------------------------------------------ + +#define IP_FORWARD defined(CYGFUN_LWIP_IP_FORWARD) +#define IP_OPTIONS_ALLOWED defined(CYGFUN_LWIP_IP_OPTIONS_ALLOWED) +#define IP_REASSEMBLY defined(CYGFUN_LWIP_IP_REASSEMBLY) +#define IP_FRAG defined(CYGFUN_LWIP_IP_FRAG) +#define IP_REASS_MAXAGE CYGNUM_LWIP_IP_REASS_MAXAGE +#define IP_REASS_MAX_PBUFS CYGNUM_LWIP_IP_REASS_MAX_PBUFS +#define IP_FRAG_USES_STATIC_BUF defined(CYGFUN_LWIP_IP_FRAG_USES_STATIC_BUF) +#ifdef CYGFUN_LWIP_IP_FRAG_USES_STATIC_BUF +# define IP_FRAG_MAX_MTU CYGNUM_LWIP_IP_FRAG_MAX_MTU +#endif +#define IP_DEFAULT_TTL CYGNUM_LWIP_IP_DEFAULT_TTL +#define IP_SOF_BROADCAST defined(CYGFUN_LWIP_IP_SOF_BROADCAST) +#define IP_SOF_BROADCAST_RECV defined(CYGFUN_LWIP_IP_SOF_BROADCAST_RECV) +//------------------------------------------------------------------------------ +// ICMP options +//------------------------------------------------------------------------------ -/* The following four are used only with the sequential API and can be - set to 0 if the application only will use the raw API. */ -/* MEMP_NUM_NETBUF: the number of struct netbufs. */ -#define MEMP_NUM_NETBUF CYGNUM_LWIP_MEMP_NUM_NETBUF -/* MEMP_NUM_NETCONN: the number of struct netconns. */ -#define MEMP_NUM_NETCONN CYGNUM_LWIP_MEMP_NUM_NETCONN -/* MEMP_NUM_APIMSG: the number of struct api_msg, used for - communication between the TCP/IP stack and the sequential - programs. */ -#define MEMP_NUM_API_MSG CYGNUM_LWIP_MEMP_NUM_APIMSG -/* MEMP_NUM_TCPIPMSG: the number of struct tcpip_msg, which is used - for sequential API communication and incoming packets. Used in - src/api/tcpip.c. */ -#define MEMP_NUM_TCPIP_MSG CYGNUM_LWIP_MEMP_NUM_TCPIP_MSG +#ifdef CYGPKG_LWIP_ICMP +# define LWIP_ICMP 1 +# define ICMP_TTL CYGNUM_LWIP_ICMP_TTL +# define LWIP_BROADCAST_PING defined(CYGFUN_LWIP_BROADCAST_PING) +# define LWIP_MULTICAST_PING defined(CYGFUN_LWIP_MULTICAST_PING) +#endif -/* ---------- Pbuf options ---------- */ -/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ -#define PBUF_POOL_SIZE CYGNUM_LWIP_PBUF_POOL_SIZE +//------------------------------------------------------------------------------ +// RAW options +//------------------------------------------------------------------------------ + +#ifdef CYGPKG_LWIP_RAW +# define LWIP_RAW 1 +# define RAW_TTL CYGNUM_LWIP_RAW_TTL +#endif -/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ -#define PBUF_POOL_BUFSIZE CYGNUM_LWIP_PBUF_POOL_BUFSIZE +//------------------------------------------------------------------------------ +// DHCP options +//------------------------------------------------------------------------------ -/* PBUF_LINK_HLEN: the number of bytes that should be allocated for a - link level header. */ -#define PBUF_LINK_HLEN CYGNUM_LWIP_PBUF_LINK_HLEN - -/* ---------- TCP options ---------- */ -#define LWIP_TCP defined(CYGPKG_LWIP_TCP) -#define TCP_TTL CYGNUM_LWIP_TCP_TTL +#ifdef CYGPKG_LWIP_DHCP +# define LWIP_DHCP 1 +# ifdef CYGPKG_LWIP_ARP +# define DHCP_DOES_ARP_CHECK defined(CYGFUN_LWIP_DHCP_DOES_ARP_CHECK) +# endif +#endif -/* Controls if TCP should queue segments that arrive out of - order. Define to 0 if your device is low on memory. */ -#define TCP_QUEUE_OOSEQ defined(CYGIMP_LWIP_TCP_QUEUE_OOSEQ) - -/* TCP Maximum segment size. */ -#define TCP_MSS CYGNUM_LWIP_TCP_MSS +//------------------------------------------------------------------------------ +// AUTOIP options +//------------------------------------------------------------------------------ -/* TCP sender buffer space (bytes). */ -#define TCP_SND_BUF CYGNUM_LWIP_TCP_SND_BUF -#define TCP_SNDLOWAT TCP_SND_BUF/2 +#ifdef CYGPKG_LWIP_AUTOIP +# define LWIP_AUTOIP 1 +# ifdef CYGPKG_LWIP_DHCP +# define LWIP_DHCP_AUTOIP_COOP defined(CYGFUN_LWIP_DHCP_AUTOIP_COOP) +# endif +#endif + +//------------------------------------------------------------------------------ +// SNMP options +//------------------------------------------------------------------------------ -/* TCP sender buffer space (pbufs). This must be at least = 2 * - TCP_SND_BUF/TCP_MSS for things to work. */ -#define TCP_SND_QUEUELEN CYGNUM_LWIP_TCP_SND_QUEUELEN +#ifdef CYGPKG_LWIP_SNMP +# define LWIP_SNMP 1 +# define SNMP_CONCURRENT_REQUESTS CYGNUM_LWIP_SNMP_CONCURRENT_REQUESTS +# define SNMP_TRAP_DESTINATIONS CYGNUM_LWIP_SNMP_TRAP_DESTINATIONS +# define SNMP_PRIVATE_MIB defined(CYGFUN_LWIP_SNMP_PRIVATE_MIB) +# define SNMP_SAFE_REQUESTS defined(CYGFUN_LWIP_SNMP_SAFE_REQUESTS) +#endif -/* TCP receive window. */ -#define TCP_WND CYGNUM_LWIP_TCP_WND +//------------------------------------------------------------------------------ +// IGMP options +//------------------------------------------------------------------------------ -/* Maximum number of retransmissions of data segments. */ -#define TCP_MAXRTX CYGNUM_LWIP_TCP_MAXRTX +#ifdef CYGPKG_LWIP_IGMP +# define LWIP_IGMP 1 +#endif -/* Maximum number of retransmissions of SYN segments. */ -#define TCP_SYNMAXRTX CYGNUM_LWIP_TCP_SYNMAXRTX - -/* ---------- ARP options ---------- */ -#define ARP_TABLE_SIZE CYGNUM_LWIP_ARP_TABLE_SIZE +//------------------------------------------------------------------------------ +// DNS options +//------------------------------------------------------------------------------ -/* ---------- IP options ---------- */ -/* Define IP_FORWARD to 1 if you wish to have the ability to forward - IP packets across network interfaces. If you are going to run lwIP - on a device with only one network interface, define this to 0. */ -#define IP_FORWARD defined(CYGFUN_LWIP_IP_FORWARD) +#ifdef CYGPKG_LWIP_DNS +# define LWIP_DNS 1 +# define DNS_TABLE_SIZE CYGNUM_LWIP_DNS_TABLE_SIZE +# define DNS_MAX_NAME_LENGTH CYGNUM_LWIP_DNS_MAX_NAME_LENGTH +# define DNS_MAX_SERVERS CYGNUM_LWIP_DNS_MAX_SERVERS +# define DNS_DOES_NAME_CHECK defined(CYGFUN_LWIP_DNS_DOES_NAME_CHECK) +# define DNS_USES_STATIC_BUF CYGNUM_LWIP_DNS_USES_STATIC_BUF +# define DNS_MSG_SIZE CYGNUM_LWIP_DNS_MSG_SIZE +# ifdef CYGPKG_LWIP_DNS_LOCAL_HOSTLIST +# define DNS_LOCAL_HOSTLIST 1 +# define DNS_LOCAL_HOSTLIST_INIT CYGDAT_LWIP_DNS_LOCAL_HOSTLIST_INIT +# define DNS_LOCAL_HOSTLIST_IS_DYNAMIC defined(CYGFUN_LWIP_DNS_LOCAL_HOSTLIST_IS_DYNAMIC) +# endif +#endif + +//------------------------------------------------------------------------------ +// UDP options +//------------------------------------------------------------------------------ - -/* If defined to 1, IP options are allowed (but not parsed). If - defined to 0, all packets with IP options are dropped. */ -#define IP_OPTIONS defined(CYGFUN_LWIP_IP_OPTIONS) +#ifdef CYGPKG_LWIP_UDP +# define LWIP_UDP 1 +# define LWIP_UDPLITE defined(CYGFUN_LWIP_UDPLITE) +# define UDP_TTL CYGNUM_LWIP_UDP_TTL +#endif - -/* ---------- ICMP options ---------- */ -#define ICMP_TTL CYGNUM_LWIP_ICMP_TTL - +//------------------------------------------------------------------------------ +// TCP options +//------------------------------------------------------------------------------ -/* ---------- DHCP options ---------- */ -/* Define LWIP_DHCP to 1 if you want DHCP configuration of - interfaces.*/ -#define LWIP_DHCP defined(CYGPKG_LWIP_DHCP) -#define DHCP_DOES_ARP_CHECK defined(CYGOPT_LWIP_DHCP_DOES_ARP_CHECK) - - -/* ---------- UDP options ---------- */ -#define LWIP_UDP defined(CYGPKG_LWIP_UDP) -#define UDP_TTL CYGNUM_LWIP_UDP_TTL +#ifdef CYGPKG_LWIP_TCP +# define LWIP_TCP 1 +# define TCP_TTL CYGNUM_LWIP_TCP_TTL +# define TCP_WND CYGNUM_LWIP_TCP_WND +# define TCP_MAXRTX CYGNUM_LWIP_TCP_MAXRTX +# define TCP_SYNMAXRTX CYGNUM_LWIP_TCP_SYNMAXRTX +# define TCP_QUEUE_OOSEQ defined(CYGFUN_LWIP_TCP_QUEUE_OOSEQ) +# define TCP_MSS CYGNUM_LWIP_TCP_MSS +# define TCP_CALCULATE_EFF_SEND_MSS defined(CYGFUN_LWIP_TCP_CALCULATE_EFF_SEND_MSS) +# define TCP_SND_BUF CYGNUM_LWIP_TCP_SND_BUF +# define TCP_SND_QUEUELEN CYGNUM_LWIP_TCP_SND_QUEUELEN +# define TCP_SNDLOWAT CYGNUM_LWIP_TCP_SNDLOWAT +# ifdef CYGFUN_LWIP_TCP_LISTEN_BACKLOG +# define TCP_LISTEN_BACKLOG 1 +# define TCP_DEFAULT_LISTEN_BACKLOG CYGNUM_LWIP_TCP_DEFAULT_LISTEN_BACKLOG +# endif +# define LWIP_TCP_TIMESTAMPS defined(CYGFUN_LWIP_TCP_TIMESTAMPS) +# define TCP_WND_UPDATE_THRESHOLD CYGNUM_LWIP_TCP_WND_UPDATE_THRESHOLD +# ifdef CYGFUN_LWIP_EVENT_API +# define LWIP_EVENT_API 1 +# endif +# ifdef CYGFUN_LWIP_CALLBACK_API +# define LWIP_CALLBACK_API 1 +# endif +#endif -/* ---------- RAW socket support ---------- */ -#define LWIP_RAW defined(CYGFUN_LWIP_RAW) +//------------------------------------------------------------------------------ +// Pbuf options +//------------------------------------------------------------------------------ -/* ---------- SLIP options --------- */ -#define LWIP_SLIP defined(CYGPKG_LWIP_SLIP) -#define SLIP_DEV CYGDAT_LWIP_SLIP_DEV +#define PBUF_LINK_HLEN CYGNUM_LWIP_PBUF_LINK_HLEN +#define PBUF_POOL_BUFSIZE CYGNUM_LWIP_PBUF_POOL_BUFSIZE -/* ---------- LOOPIF options --------- */ -#define LWIP_HAVE_LOOPIF defined(CYGFUN_LWIP_LOOPIF) +//------------------------------------------------------------------------------ +// Network interfaces options +//------------------------------------------------------------------------------ -/* ---------- PPP options --------- */ -#define PPP_SUPPORT defined(CYGPKG_LWIP_PPP) -#define PPP_DEV CYGDAT_LWIP_PPP_DEV -#define MD5_SUPPORT 1 -#ifdef CYGIMP_LWIP_PPP_PAP_AUTH -#define PAP_SUPPORT 1 -#else -#define PAP_SUPPORT 0 +#define LWIP_NETIF_API defined(CYGPKG_LWIP_NETIF_API) +#define LWIP_NETIF_HOSTNAME defined(CYGIMP_LWIP_NETIF_HOSTNAME) +#define LWIP_NETIF_STATUS_CALLBACK defined(CYGFUN_LWIP_NETIF_STATUS_CALLBACK) +#define LWIP_NETIF_LINK_CALLBACK defined(CYGFUN_LWIP_NETIF_LINK_CALLBACK) +#define LWIP_NETIF_HWADDRHINT defined(CYGIMP_LWIP_NETIF_HWADDRHINT) +#define LWIP_NETIF_LOOPBACK defined(CYGIMP_LWIP_NETIF_LOOPBACK) +#define LWIP_LOOPBACK_MAX_PBUFS CYGIMP_LWIP_NETIF_LOOPBACK_MAX_PBUFS +//#define LWIP_NETIF_LOOPBACK_MULTITHREADING // not configurable +//#define LWIP_NETIF_TX_SINGLE_PBUF // not configurable + +//------------------------------------------------------------------------------ +// LOOPIF options +//------------------------------------------------------------------------------ + +#ifdef CYGPKG_LWIP_LOOPIF +# define LWIP_HAVE_LOOPIF 1 #endif -#ifdef CYGIMP_LWIP_PPP_CHAP_AUTH -#define CHAP_SUPPORT 1 -#else -#define CHAP_SUPPORT 0 + +//------------------------------------------------------------------------------ +// SLIPIF options +//------------------------------------------------------------------------------ + +#ifdef CYGPKG_LWIP_SLIP +# define LWIP_HAVE_SLIPIF 1 #endif +//------------------------------------------------------------------------------ +// Thread options +//------------------------------------------------------------------------------ -/* ------- Thread priorities ---------------*/ -#define TCPIP_THREAD_PRIO CYGNUM_LWIP_NETWORK_THREAD_PRIORITY -#define SLIPIF_THREAD_PRIO CYGNUM_LWIP_SLIPIF_THREAD_PRIORITY -#define PPP_THREAD_PRIO CYGNUM_LWIP_PPP_THREAD_PRIORITY +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL +# define TCPIP_THREAD_NAME "tcpip" +# define TCPIP_THREAD_PRIO CYGNUM_LWIP_THREAD_TCPIP_PRIORITY +//# define TCPIP_MBOX_SIZE // not configurable +# if CYGNUM_LWIP_THREAD_TCPIP_STACKSIZE < CYGNUM_HAL_STACK_SIZE_MINIMUM +# define TCPIP_THREAD_STACKSIZE CYGNUM_HAL_STACK_SIZE_MINIMUM +# else +# define TCPIP_THREAD_STACKSIZE CYGNUM_LWIP_THREAD_TCPIP_STACKSIZE +# endif +#else +# define TCPIP_THREAD_STACKSIZE 0 +#endif +#if defined(CYGFUN_LWIP_MODE_SEQUENTIAL) && defined(CYGPKG_LWIP_SLIP) +# define SLIPIF_THREAD_NAME "slipif" +# if CYGNUM_LWIP_THREAD_SLIP_STACKSIZE < CYGNUM_HAL_STACK_SIZE_MINIMUM +# define SLIPIF_THREAD_STACKSIZE CYGNUM_HAL_STACK_SIZE_MINIMUM +# else +# define SLIPIF_THREAD_STACKSIZE CYGNUM_LWIP_THREAD_SLIP_STACKSIZE +# endif +# define SLIPIF_THREAD_PRIO CYGNUM_LWIP_THREAD_SLIP_PRIORITY +#else +# define SLIPIF_THREAD_STACKSIZE 0 +#endif + +#if defined(CYGFUN_LWIP_MODE_SEQUENTIAL) && defined(CYGFUN_LWIP_PPPOS_SUPPORT) +# define PPP_THREAD_NAME "ppp" +# if CYGNUM_LWIP_THREAD_PPPOS_STACKSIZE < CYGNUM_HAL_STACK_SIZE_MINIMUM +# define PPP_THREAD_STACKSIZE CYGNUM_HAL_STACK_SIZE_MINIMUM +# else +# define PPP_THREAD_STACKSIZE CYGNUM_LWIP_THREAD_PPPOS_STACKSIZE +# endif +# define PPP_THREAD_PRIO CYGNUM_LWIP_THREAD_PPPOS_PRIORITY +#else +# define PPP_THREAD_STACKSIZE 0 +#endif + +#if defined(CYGFUN_LWIP_MODE_SEQUENTIAL) && defined(CYGPKG_LWIP_ETH) +# define ETH_THREAD_NAME "eth" +# if CYGNUM_LWIP_THREAD_ETH_STACKSIZE < CYGNUM_HAL_STACK_SIZE_MINIMUM +# define ETH_THREAD_STACKSIZE CYGNUM_HAL_STACK_SIZE_MINIMUM +# else +# define ETH_THREAD_STACKSIZE CYGNUM_LWIP_THREAD_ETH_STACKSIZE +# endif +# define ETH_THREAD_PRIO CYGNUM_LWIP_THREAD_ETH_PRIORITY +#else +# define ETH_THREAD_STACKSIZE 0 +#endif -/* ---------- Statistics options ---------- */ -#define LWIP_STATS defined(CYGDBG_LWIP_STATS) +//#define DEFAULT_THREAD_NAME // not configurable +//#define DEFAULT_THREAD_STACKSIZE // not configurable +//#define DEFAULT_THREAD_PRIO // not configurable +//#define DEFAULT_RAW_RECVMBOX_SIZE // not configurable +//#define DEFAULT_UDP_RECVMBOX_SIZE // not configurable +//#define DEFAULT_TCP_RECVMBOX_SIZE // not configurable +//#define DEFAULT_ACCEPTMBOX_SIZE // not configurable +//------------------------------------------------------------------------------ +// Sequential layer options +//------------------------------------------------------------------------------ + +//#define LWIP_TCPIP_CORE_LOCKING // not configurable +#define LWIP_NETCONN defined(CYGPKG_LWIP_NETCONN_API) + +//------------------------------------------------------------------------------ +// Socket options +//------------------------------------------------------------------------------ -// --------- Sockets options -------------- -#define LWIP_COMPAT_SOCKETS defined(CYGFUN_LWIP_COMPAT_SOCKETS) +#ifdef CYGPKG_LWIP_SOCKET_API +# define LWIP_SOCKET 1 +# define LWIP_COMPAT_SOCKETS defined(CYGFUN_LWIP_COMPAT_SOCKETS) +# define LWIP_POSIX_SOCKETS_IO_NAMES defined(CYGFUN_LWIP_POSIX_SOCKETS_IO_NAMES) +# define LWIP_TCP_KEEPALIVE defined(CYGFUN_LWIP_TCP_KEEPALIVE) +# define LWIP_SO_RCVTIMEO defined(CYGFUN_LWIP_SO_RCVTIMEO) +# define LWIP_SO_RCVBUF defined(CYGFUN_LWIP_SO_RCVBUF) +//# define RECV_BUFSIZE_DEFAULT // not configurable +//# define SO_REUSE // not configurable +#else +# define LWIP_SOCKET 0 +#endif +//------------------------------------------------------------------------------ +// Statistics options +//------------------------------------------------------------------------------ -/* ---------- Debug options ---------- */ -#if !defined(CYGDBG_LWIP_ASSERTS) -#define LWIP_NOASSERT +#ifdef CYGPKG_LWIP_STATS +# define LWIP_STATS 1 +# define LWIP_STATS_DISPLAY 1 +# define LINK_STATS defined(CYGVAR_LWIP_LINK_STATS) +# ifdef CYGPKG_LWIP_ARP +# define ETHARP_STATS defined(CYGVAR_LWIP_ETHARP_STATS) +# endif +# define IP_STATS defined(CYGVAR_LWIP_IP_STATS) +# if defined(CYGFUN_LWIP_IP_REASSEMBLY) || defined(CYGFUN_LWIP_IP_FRAG) +# define IPFRAG_STATS defined(CYGVAR_LWIP_IPFRAG_STATS) +# endif +# ifdef CYGPKG_LWIP_ICMP +# define ICMP_STATS defined(CYGVAR_LWIP_ICMP_STATS) +# endif +# ifdef CYGPKG_LWIP_IGMP +# define IGMP_STATS defined(CYGVAR_LWIP_IGMP_STATS) +# endif +# ifdef CYGPKG_LWIP_UDP +# define UDP_STATS defined(CYGVAR_LWIP_UDP_STATS) +# endif +# ifdef CYGPKG_LWIP_TCP +# define TCP_STATS defined(CYGVAR_LWIP_TCP_STATS) +# endif +# define MEM_STATS defined(CYGVAR_LWIP_MEM_STATS) +# define MEMP_STATS defined(CYGVAR_LWIP_MEMP_STATS) +# define SYS_STATS defined(CYGVAR_LWIP_SYS_STATS) #endif -#if defined(CYGDBG_LWIP_DEBUG) -#define LWIP_DEBUG -#define MEM_DEBUG DBG_ON -#define MEMP_DEBUG DBG_ON -#define PBUF_DEBUG DBG_ON -#define API_LIB_DEBUG DBG_ON -#define API_MSG_DEBUG DBG_ON -#define TCPIP_DEBUG DBG_ON -#define NETIF_DEBUG DBG_ON -#define SOCKETS_DEBUG DBG_ON -#define DEMO_DEBUG DBG_ON -#define IP_DEBUG DBG_ON -#define IP_REASS_DEBUG DBG_ON -#define RAW_DEBUG DBG_ON -#define ICMP_DEBUG DBG_ON -#define UDP_DEBUG DBG_ON -#define TCP_DEBUG DBG_ON -#define TCP_INPUT_DEBUG DBG_ON -#define TCP_OUTPUT_DEBUG DBG_ON -#define TCP_RTO_DEBUG DBG_ON -#define TCP_CWND_DEBUG DBG_ON -#define TCP_WND_DEBUG DBG_ON -#define TCP_FR_DEBUG DBG_ON -#define TCP_QLEN_DEBUG DBG_ON -#define TCP_RST_DEBUG DBG_ON -#define PPP_DEBUG DBG_ON +//------------------------------------------------------------------------------ +// PPP options +//------------------------------------------------------------------------------ -#define DBG_TYPES_ON (DBG_ON|DBG_TRACE|DBG_STATE|DBG_FRESH|DBG_HALT) +#ifdef CYGPKG_LWIP_PPP +# define PPP_SUPPORT 1 +# define PPPOE_SUPPORT defined(CYGFUN_LWIP_PPPOE_SUPPORT) +# define PPPOS_SUPPORT defined(CYGFUN_LWIP_PPPOS_SUPPORT) +# define NUM_PPP CYGNUM_LWIP_NUM_PPP +# ifdef CYGFUN_LWIP_PAP_SUPPORT +# define PAP_SUPPORT 1 +# else +# define PAP_SUPPORT 0 +# endif +# ifdef CYGFUN_LWIP_CHAP_SUPPORT +# define CHAP_SUPPORT 1 +# else +# define CHAP_SUPPORT 0 +# endif +//# define MSCHAP_SUPPORT // not supported yet +//# define CBCP_SUPPORT // not supported yet +//# define CCP_SUPPORT // not supported yet +# define VJ_SUPPORT defined(CYGFUN_LWIP_VJ_SUPPORT) +# define MD5_SUPPORT defined(CYGFUN_LWIP_MD5_SUPPORT) +# define RECORD_SUPPORT defined(CYGFUN_LWIP_RECORD_SUPPORT) + +//# define FSM_DEFTIMEOUT // not configurable +//# define FSM_DEFMAXTERMREQS // not configurable +//# define FSM_DEFMAXCONFREQS // not configurable +//# define FSM_DEFMAXNAKLOOPS // not configurable +//# define UPAP_DEFTIMEOUT // not configurable +//# define UPAP_DEFREQTIME // not configurable +//# define CHAP_DEFTIMEOUT // not configurable +//# define CHAP_DEFTRANSMITS // not configurable +//# define LCP_ECHOINTERVAL // not configurable +//# define LCP_MAXECHOFAILS // not configurable +//# define PPP_MAXIDLEFLAG // not configurable +//# define PPP_MAXMTU // not configurable +//# define PPP_DEFMRU // not configurable #endif +//------------------------------------------------------------------------------ +// Checksum options +//------------------------------------------------------------------------------ +#define CHECKSUM_GEN_IP defined(CYGIMP_LWIP_CHECKSUM_GEN_IP) +#define CHECKSUM_GEN_UDP defined(CYGIMP_LWIP_CHECKSUM_GEN_UDP) +#define CHECKSUM_GEN_TCP defined(CYGIMP_LWIP_CHECKSUM_GEN_TCP) +#define CHECKSUM_CHECK_IP defined(CYGIMP_LWIP_CHECKSUM_CHECK_IP) +#define CHECKSUM_CHECK_UDP defined(CYGIMP_LWIP_CHECKSUM_CHECK_UDP) +#define CHECKSUM_CHECK_TCP defined(CYGIMP_LWIP_CHECKSUM_CHECK_TCP) + +//------------------------------------------------------------------------------ +// Debugging options +//------------------------------------------------------------------------------ -#endif /* __LWIPOPTS_H__ */ +#ifdef CYGDBG_LWIP_DEBUG +# define LWIP_DEBUG 1 +// Modules +# ifdef CYGDBG_LWIP_DEBUG_ETHARP +# define ETHARP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_NETIF +# define NETIF_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_PBUF +# define PBUF_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_API +# define API_LIB_DEBUG LWIP_DBG_ON +# define API_MSG_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_SOCKETS +# define SOCKETS_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_ICMP +# define ICMP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_IGMP +# define IGMP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_IP +# define INET_DEBUG LWIP_DBG_ON +# define IP_DEBUG LWIP_DBG_ON +# define IP_REASS_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_RAW +# define RAW_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_MEM +# define MEM_DEBUG LWIP_DBG_ON +# define MEMP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_SYS +# define SYS_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_TCP +# define TCP_DEBUG LWIP_DBG_ON +# define TCP_INPUT_DEBUG LWIP_DBG_ON +# define TCP_FR_DEBUG LWIP_DBG_ON +# define TCP_RTO_DEBUG LWIP_DBG_ON +# define TCP_CWND_DEBUG LWIP_DBG_ON +# define TCP_WND_DEBUG LWIP_DBG_ON +# define TCP_OUTPUT_DEBUG LWIP_DBG_ON +# define TCP_RST_DEBUG LWIP_DBG_ON +# define TCP_QLEN_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_UDP +# define UDP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_TCPIP +# define TCPIP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_PPP +# define PPP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_SLIP +# define SLIP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_DHCP +# define DHCP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_AUTOIP +# define AUTOIP_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_SNMP +# define SNMP_MSG_DEBUG LWIP_DBG_ON +# define SNMP_MIB_DEBUG LWIP_DBG_ON +# endif +# ifdef CYGDBG_LWIP_DEBUG_DNS +# define DNS_DEBUG LWIP_DBG_ON +# endif +// Log level +# define LWIP_DBG_MIN_LEVEL CYGDBG_LWIP_DEBUG_LEVEL +// Log types +# ifdef CYGDBG_LWIP_DEBUG_TRACE +# define __LWIP_DBG_TRACE LWIP_DBG_TRACE +# else +# define __LWIP_DBG_TRACE 0 +# endif +# ifdef CYGDBG_LWIP_DEBUG_STATE +# define __LWIP_DBG_STATE LWIP_DBG_STATE +# else +# define __LWIP_DBG_STATE 0 +# endif +# ifdef CYGDBG_LWIP_DEBUG_FRESH +# define __LWIP_DBG_FRESH LWIP_DBG_FRESH +# else +# define __LWIP_DBG_FRESH 0 +# endif +# define LWIP_DBG_TYPES_ON (LWIP_DBG_ON | __LWIP_DBG_TRACE | \ + __LWIP_DBG_STATE | __LWIP_DBG_FRESH) +#endif + +#endif // CYGONCE_LWIPOPTS_H diff --git a/packages/net/lwip_tcpip/current/include/netif/etharp.h b/packages/net/lwip_tcpip/current/include/netif/etharp.h --- a/packages/net/lwip_tcpip/current/include/netif/etharp.h +++ b/packages/net/lwip_tcpip/current/include/netif/etharp.h @@ -35,21 +35,33 @@ #ifndef __NETIF_ETHARP_H__ #define __NETIF_ETHARP_H__ -#ifndef ETH_PAD_SIZE -#define ETH_PAD_SIZE 0 -#endif +#include "lwip/opt.h" + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ #include "lwip/pbuf.h" #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/ip.h" +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif PACK_STRUCT_BEGIN struct eth_addr { - PACK_STRUCT_FIELD(u8_t addr[6]); + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); } PACK_STRUCT_STRUCT; PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES @@ -73,13 +85,34 @@ PACK_STRUCT_END # include "arch/epstruct.h" #endif +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#if ETHARP_SUPPORT_VLAN + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_vlan_hdr { + PACK_STRUCT_FIELD(u16_t tpid); + PACK_STRUCT_FIELD(u16_t prio_vid); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_VLAN_HDR 4 +#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF) + +#endif /* ETHARP_SUPPORT_VLAN */ + #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif PACK_STRUCT_BEGIN /** the ARP message */ struct etharp_hdr { - PACK_STRUCT_FIELD(struct eth_hdr ethhdr); PACK_STRUCT_FIELD(u16_t hwtype); PACK_STRUCT_FIELD(u16_t proto); PACK_STRUCT_FIELD(u16_t _hwlen_protolen); @@ -94,33 +127,66 @@ PACK_STRUCT_END # include "arch/epstruct.h" #endif -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/bpstruct.h" -#endif -PACK_STRUCT_BEGIN -struct ethip_hdr { - PACK_STRUCT_FIELD(struct eth_hdr eth); - PACK_STRUCT_FIELD(struct ip_hdr ip); -} PACK_STRUCT_STRUCT; -PACK_STRUCT_END -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/epstruct.h" -#endif +#define SIZEOF_ETHARP_HDR 28 +#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) /** 5 seconds period */ #define ARP_TMR_INTERVAL 5000 -#define ETHTYPE_ARP 0x0806 -#define ETHTYPE_IP 0x0800 +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 +#define ETHTYPE_VLAN 0x8100 +#define ETHTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 -void etharp_init(void); +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, not init needed. */ void etharp_tmr(void); +s8_t etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr, + struct eth_addr **eth_ret, struct ip_addr **ip_ret); void etharp_ip_input(struct netif *netif, struct pbuf *p); void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p); -err_t etharp_output(struct netif *netif, struct ip_addr *ipaddr, - struct pbuf *q); +err_t etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr); err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q); err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr); +/** For Ethernet network interfaces, we might want to send "gratuitous ARP"; + * this is an ARP packet sent by a node in order to spontaneously cause other + * nodes to update an entry in their ARP cache. + * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ +#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#if LWIP_AUTOIP +err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const struct ip_addr *ipsrc_addr, + const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, + const u16_t opcode); +#endif /* LWIP_AUTOIP */ + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP */ #endif /* __NETIF_ARP_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/netif/loopif.h b/packages/net/lwip_tcpip/current/include/netif/loopif.h --- a/packages/net/lwip_tcpip/current/include/netif/loopif.h +++ b/packages/net/lwip_tcpip/current/include/netif/loopif.h @@ -32,8 +32,22 @@ #ifndef __NETIF_LOOPIF_H__ #define __NETIF_LOOPIF_H__ +#include "lwip/opt.h" #include "lwip/netif.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +#define loopif_poll netif_poll +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ err_t loopif_init(struct netif *netif); +#ifdef __cplusplus +} +#endif + #endif /* __NETIF_LOOPIF_H__ */ diff --git a/packages/net/lwip_tcpip/current/include/netif/ppp/chat.h b/packages/net/lwip_tcpip/current/include/netif/ppp/chat.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/netif/ppp/chat.h @@ -0,0 +1,115 @@ +/** + * @file + * Chat interface + * + */ + +/* + * Copyright (c) 2001-2004 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. + * + * Author: Simon Kallweit + */ + +/* + * This is an arch independent chat implementation. The specific serial hooks + * must be provided by another file. They are sio_open, sio_recv and sio_send. + */ + +#ifndef CHAT_H +#define CHAT_H + +#include "lwip/sio.h" + +#define CHAT_TMR_INTERVAL 500 /**< Chat timer interval in ms */ + +/** Chat commands */ +typedef enum { + CHAT_ABORT, /**< Defines a string which aborts the chat conversation */ + CHAT_SAY, /**< Prints an informational text */ + CHAT_SEND, /**< Sends a command to the modem */ + CHAT_SEND_CB, /**< Sends a command to the modem (gets string via callback) */ + CHAT_WAIT, /**< Waits for an answer from the modem */ + CHAT_SLEEP, /**< Sleeps for at least 1 second */ + CHAT_LAST, /**< Last item in chat table */ +} chat_cmd_t; + +/** Chat item */ +struct chat_item { + chat_cmd_t cmd; /**< Chat command */ + char *arg; /**< Command argument */ +}; + +/** Chat error codes */ +typedef enum { + CHAT_ERR_OK, /**< Chat finished successfully */ + CHAT_ERR_TIMEOUT, /**< Chat timeout */ + CHAT_ERR_ABORT, /**< Chat aborted */ +} chat_err_t; + +/** Chat context */ +typedef struct chat *chat_t; + +/** Chat callback */ +typedef void (*chat_cb_t)(chat_t chat, chat_err_t err, void *arg); + +/** Chat send callback */ +typedef void (*chat_send_cb_t)(chat_t chat, int id, char *buf, size_t len, void *arg); + +/** + * Creates a new chat. The chat is further processed by calling chat_poll(). + * The result of the chat is posted by the chat_cb() callback function. + * @param sd Serial io descriptor + * @param items Chat items + * @param timeout Timeout in seconds + * @param chat_cb Chat result callback + * @param send_cb Chat send callback + * @param arg Argument used in callback + * @return Returns the newly allocated chat, or NULL if no memory was available. + */ +chat_t chat_new(sio_fd_t sd, const struct chat_item *items, int timeout, + chat_cb_t chat_cb, chat_send_cb_t send_cb, void *arg); + +/** + * Frees a chat. + * @param chat Chat + */ +void chat_free(chat_t chat); + +/** + * Processes the chat. This needs to be called iteratively until the chat_cb() + * callback function posts success or an error. + * @param chat Chat + */ +void chat_poll(chat_t chat); + +/** + * This should be called every CHAT_TMR_INTERVAL milliseconds. + */ +void chat_tmr(void); + +#endif /* CHAT_H */ diff --git a/packages/net/lwip_tcpip/current/include/netif/ppp/ppp.h b/packages/net/lwip_tcpip/current/include/netif/ppp/ppp.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/netif/ppp/ppp.h @@ -0,0 +1,470 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE 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 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. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/api.h" +#include "lwip/sockets.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/tcpip.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define PPP_TMR_INTERVAL 100 + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u8_t ext_accm[32]; + +/* + * What to do with network protocol (NP) packets. + */ +enum ppp_np_mode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u8_t *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u8_t) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp); (cp)++; (s) <<= 8; \ + (s) |= *(cp); (cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u8_t) ((s) >> 8); \ + *(cp)++ = (u8_t) (s & 0xff); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u8_t) ((l) >> 24); \ + *(cp)++ = (u8_t) ((l) >> 16); \ + *(cp)++ = (u8_t) ((l) >> 8); \ + *(cp)++ = (u8_t) (l); \ +} + + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#define BCMP(s0, s1, l) memcmp((u8_t *)(s0), (u8_t *)(s1), (l)) +#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) +#define BZERO(s, n) memset(s, 0, n) + +#if PPP_DEBUG +#define PRINTMSG(m, l) { m[l] = '\0'; ppp_trace(LOG_INFO, "Remote message: %s\n", m); } +#else /* PPP_DEBUG */ +#define PRINTMSG(m, l) +#endif /* PPP_DEBUG */ + +/* + * MAKEHEADER - Add PPP Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM -1 /* Invalid parameter. */ +#define PPPERR_OPEN -2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC -4 /* Unable to allocate resources. */ +#define PPPERR_USER -5 /* User interrupt. */ +#define PPPERR_CONNECT -6 /* Connection lost. */ +#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */ + +/* + * PPP IOCTL commands. + */ +/* + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */ +#define PPPCTLS_ERRCODE 101 /* Set the error code */ +#define PPPCTLG_ERRCODE 102 /* Get the error code */ +#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u16_t protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (int unit); + /* Process a received packet */ + void (*input) (int unit, u8_t *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (int unit); + /* Lower layer has come up */ + void (*lowerup) (int unit); + /* Lower layer has gone down */ + void (*lowerdown) (int unit); + /* Open the protocol */ + void (*open) (int unit); + /* Close the protocol */ + void (*close) (int unit, char *reason); +#if 0 + /* Print a packet in readable form */ + int (*printpkt) (u8_t *pkt, int len, void (*printer) (void *, char *, ...), + void *arg); + /* Process a received data packet */ + void (*datainput) (int unit, u8_t *pkt, int len); +#endif + int enabled_flag; /* 0 if protocol is disabled */ + char *name; /* Text name of protocol */ +#if 0 + /* Check requested options, assign defaults */ + void (*check_options) (u32_t); + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u8_t *pkt, int len); +#endif +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + u16_t xmit_idle; /* seconds since last NP packet sent */ + u16_t recv_idle; /* seconds since last NP packet received */ +}; + +struct ppp_settings { + u32_t disable_defaultip : 1; /* Don't use hostname for default IP addrs */ + u32_t auth_required : 1; /* Peer is required to authenticate */ + u32_t explicit_remote : 1; /* remote_name specified with remotename opt */ + u32_t refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ + u32_t refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ + u32_t usehostname : 1; /* Use hostname for our_name */ + u32_t usepeerdns : 1; /* Ask peer for DNS adds */ + + u16_t idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + char user [MAXNAMELEN + 1]; /* Username for PAP */ + char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +}; + +struct ppp_addrs { + struct ip_addr our_ipaddr, his_ipaddr, netmask, dns1, dns2; +}; + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +/* Buffers for outgoing packets. */ +extern u8_t *outpacket_buf[NUM_PPP]; + +extern struct ppp_settings ppp_settings; + +extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* PPP link status callback */ +typedef void (*ppp_status_cb_t)(void *ctx, int err, void *arg); + + +/* Initialize the PPP subsystem. */ +err_t ppp_init(void); + +/** + * This should be called every PPP_TMR_INTERVAL milliseconds. + */ +void ppp_tmr(void); + +/* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ +enum ppp_auth_type { + PPPAUTHTYPE_NONE, + PPPAUTHTYPE_ANY, + PPPAUTHTYPE_PAP, + PPPAUTHTYPE_CHAP +}; + +void ppp_set_auth(enum ppp_auth_type auth_type, const char *user, const char *passwd); + +/* + * Open a new PPP connection using the given serial I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + */ +int ppp_open_serial(sio_fd_t fd, ppp_status_cb_t status_cb, void *status_ctx); + + +/* + * Open a new PPP Over Ethernet (PPPOE) connection. + */ +int ppp_open_ethernet(struct netif *ethif, + const char *service_name, const char *concentrator_name, + ppp_status_cb_t status_cb, void *status_ctx); + + +void ppp_poll(int pd); + +/* + * Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int ppp_close(int pd); + +/* + * Check if the PPP connection is open. + */ +int ppp_is_open(int pd); + +/* + * Indicate to the PPP process that the line has disconnected. + */ +void ppp_sig_hup(int pd); + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +int ppp_ioctl(int pd, int cmd, void *arg); + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u32_t ppp_mtu(int pd); + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + */ +int ppp_write(int pd, const u8_t *s, int n); + +void ppp_in_proc_over_ethernet(int pd, struct pbuf *pb); + +struct pbuf *ppp_single_buf(struct pbuf *p); + +void ppp_link_terminated(int pd); + +void ppp_link_down(int pd); + +void ppp_main_wakeup(int pd); + + +/* Configure i/f transmit parameters */ +void ppp_send_config (int, int, u32_t, int, int); +/* Set extended transmit ACCM */ +void ppp_set_xaccm (int, ext_accm *); +/* Configure i/f receive parameters */ +void ppp_recv_config (int, int, u32_t, int, int); +/* Find out how long link has been idle */ +int get_idle_time (int, struct ppp_idle *); + +/* Configure VJ TCP header compression */ +int sifvjcomp (int, int, int, int); +/* Configure i/f down (for IP) */ +int sifup (int); +/* Set mode for handling packets for proto */ +int sifnpmode (int u, int proto, enum ppp_np_mode mode); +/* Configure i/f down (for IP) */ +int sifdown (int); +/* Configure IP addresses for i/f */ +int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); +/* Reset i/f IP addresses */ +int cifaddr (int, u32_t, u32_t); +/* Create default route through i/f */ +int sifdefaultroute (int, u32_t, u32_t); +/* Delete default route through i/f */ +int cifdefaultroute (int, u32_t, u32_t); + +/* Get appropriate netmask for address */ +u32_t GetMask (u32_t); + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_H */ diff --git a/packages/net/lwip_tcpip/current/include/netif/ppp_oe.h b/packages/net/lwip_tcpip/current/include/netif/ppp_oe.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/include/netif/ppp_oe.h @@ -0,0 +1,161 @@ +/***************************************************************************** +* ppp_oe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE 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 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. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +struct pppoe_softc; + + +void pppoe_init(void); + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +extern int pppoe_hdrlen; + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/packages/net/lwip_tcpip/current/include/netif/slipif.h b/packages/net/lwip_tcpip/current/include/netif/slipif.h --- a/packages/net/lwip_tcpip/current/include/netif/slipif.h +++ b/packages/net/lwip_tcpip/current/include/netif/slipif.h @@ -36,7 +36,16 @@ #include "lwip/netif.h" +#ifdef __cplusplus +extern "C" { +#endif + err_t slipif_init(struct netif * netif); +void slipif_poll(struct netif *netif); + +#ifdef __cplusplus +} +#endif #endif diff --git a/packages/net/lwip_tcpip/current/include/network.h b/packages/net/lwip_tcpip/current/include/network.h --- a/packages/net/lwip_tcpip/current/include/network.h +++ b/packages/net/lwip_tcpip/current/include/network.h @@ -4,50 +4,49 @@ // // network.h // -// Misc network support +// Misc network support. // //============================================================================= -// ####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation // -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later -// version. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. // -// eCos is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// You should have received a copy of the GNU General Public License -// along with eCos; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // -// As a special exception, if other files instantiate templates or use -// macros or inline functions from this file, or you compile this file -// and link it with other works to produce a work based on this file, -// this file does not by itself cause the resulting work to be covered by -// the GNU General Public License. However the source code for this file -// must still be made available in accordance with section (3) of the GNU -// General Public License v2. +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. // -// This exception does not invalidate any other reasons why a work based -// on this file might be covered by the GNU General Public License. -// ------------------------------------------- -// ####ECOSGPLCOPYRIGHTEND#### +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### //============================================================================= //#####DESCRIPTIONBEGIN#### // -// Author(s): Jani Monoses -// Contributors: Jani Monoses -// Date: 2006-03-23 -// Purpose: -// Description: -// -// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2009-05-18 +// Purpose: +// Description: // //####DESCRIPTIONEND#### // @@ -57,18 +56,6 @@ extern "C" { #endif - -//============================================================================= -// INCLUDES -//============================================================================= -#include // lwIP stack includes -#include -#include - - -#ifdef __cplusplus -} -#endif - -//----------------------------------------------------------------------------- +#include + #endif // #ifndef CYGONCE_NETWORK_H diff --git a/packages/net/lwip_tcpip/current/src/api/api_lib.c b/packages/net/lwip_tcpip/current/src/api/api_lib.c --- a/packages/net/lwip_tcpip/current/src/api/api_lib.c +++ b/packages/net/lwip_tcpip/current/src/api/api_lib.c @@ -1,3 +1,9 @@ +/** + * @file + * Sequential API External module + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -34,708 +40,519 @@ the application */ #include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + #include "lwip/api.h" -#include "lwip/api_msg.h" +#include "lwip/tcpip.h" #include "lwip/memp.h" - -struct -netbuf *netbuf_new(void) -{ - struct netbuf *buf; +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" - buf = memp_malloc(MEMP_NETBUF); - if (buf != NULL) { - buf->p = NULL; - buf->ptr = NULL; - return buf; - } else { - return NULL; - } -} - -void -netbuf_delete(struct netbuf *buf) -{ - if (buf != NULL) { - if (buf->p != NULL) { - pbuf_free(buf->p); - buf->p = buf->ptr = NULL; - } - memp_free(MEMP_NETBUF, buf); - } -} +#include -void * -netbuf_alloc(struct netbuf *buf, u16_t size) -{ - /* Deallocate any previously allocated memory. */ - if (buf->p != NULL) { - pbuf_free(buf->p); - } - buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); - if (buf->p == NULL) { - return NULL; - } - buf->ptr = buf->p; - return buf->p->payload; -} - -void -netbuf_free(struct netbuf *buf) -{ - if (buf->p != NULL) { - pbuf_free(buf->p); - } - buf->p = buf->ptr = NULL; -} - -void -netbuf_ref(struct netbuf *buf, void *dataptr, u16_t size) -{ - if (buf->p != NULL) { - pbuf_free(buf->p); - } - buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); - buf->p->payload = dataptr; - buf->p->len = buf->p->tot_len = size; - buf->ptr = buf->p; -} - -void -netbuf_chain(struct netbuf *head, struct netbuf *tail) +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is also created. + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) { - pbuf_chain(head->p, tail->p); - head->ptr = head->p; - memp_free(MEMP_NETBUF, tail); -} - -u16_t -netbuf_len(struct netbuf *buf) -{ - return buf->p->tot_len; -} + struct netconn *conn; + struct api_msg msg; -err_t -netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) -{ - if (buf->ptr == NULL) { - return ERR_BUF; - } - *dataptr = buf->ptr->payload; - *len = buf->ptr->len; - return ERR_OK; -} + conn = netconn_alloc(t, callback); + if (conn != NULL ) { + msg.function = do_newconn; + msg.msg.msg.n.proto = proto; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); -s8_t -netbuf_next(struct netbuf *buf) -{ - if (buf->ptr->next == NULL) { - return -1; - } - buf->ptr = buf->ptr->next; - if (buf->ptr->next == NULL) { - return 1; - } - return 0; -} - -void -netbuf_first(struct netbuf *buf) -{ - buf->ptr = buf->p; -} - -void -netbuf_copy_partial(struct netbuf *buf, void *dataptr, u16_t len, u16_t offset) -{ - struct pbuf *p; - u16_t i, left; - - left = 0; - - if(buf == NULL || dataptr == NULL) { - return; - } - - /* This implementation is bad. It should use bcopy - instead. */ - for(p = buf->p; left < len && p != NULL; p = p->next) { - if (offset != 0 && offset >= p->len) { - offset -= p->len; - } else { - for(i = offset; i < p->len; ++i) { - ((u8_t *)dataptr)[left] = ((u8_t *)p->payload)[i]; - if (++left >= len) { - return; - } - } - offset = 0; + if (conn->err != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no op_completed", conn->op_completed != SYS_SEM_NULL); + LWIP_ASSERT("conn has no recvmbox", conn->recvmbox != SYS_MBOX_NULL); + LWIP_ASSERT("conn->acceptmbox shouldn't exist", conn->acceptmbox == SYS_MBOX_NULL); + sys_sem_free(conn->op_completed); + sys_mbox_free(conn->recvmbox); + memp_free(MEMP_NETCONN, conn); + return NULL; } } -} - -void -netbuf_copy(struct netbuf *buf, void *dataptr, u16_t len) -{ - netbuf_copy_partial(buf, dataptr, len, 0); -} - -struct ip_addr * -netbuf_fromaddr(struct netbuf *buf) -{ - return buf->fromaddr; -} - -u16_t -netbuf_fromport(struct netbuf *buf) -{ - return buf->fromport; -} - -struct -netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u16_t proto, - void (*callback)(struct netconn *, enum netconn_evt, u16_t len)) -{ - struct netconn *conn; - struct api_msg *msg; - - conn = memp_malloc(MEMP_NETCONN); - if (conn == NULL) { - return NULL; - } - - conn->err = ERR_OK; - conn->type = t; - conn->pcb.tcp = NULL; - - if ((conn->mbox = sys_mbox_new()) == SYS_MBOX_NULL) { - memp_free(MEMP_NETCONN, conn); - return NULL; - } - conn->recvmbox = SYS_MBOX_NULL; - conn->acceptmbox = SYS_MBOX_NULL; - conn->sem = SYS_SEM_NULL; - conn->state = NETCONN_NONE; - conn->socket = 0; - conn->callback = callback; - conn->recv_avail = 0; - - if((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - memp_free(MEMP_NETCONN, conn); - return NULL; - } - - msg->type = API_MSG_NEWCONN; - msg->msg.msg.bc.port = proto; /* misusing the port field */ - msg->msg.conn = conn; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); - - if ( conn->err != ERR_OK ) { - memp_free(MEMP_NETCONN, conn); - return NULL; - } - return conn; } - -struct -netconn *netconn_new(enum netconn_type t) -{ - return netconn_new_with_proto_and_callback(t,0,NULL); -} - -struct -netconn *netconn_new_with_callback(enum netconn_type t, - void (*callback)(struct netconn *, enum netconn_evt, u16_t len)) -{ - return netconn_new_with_proto_and_callback(t,0,callback); -} - - +/** + * Close a netconn 'connection' and free its resources. + * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate + * after this returns. + * + * @param conn the netconn to delete + * @return ERR_OK if the connection was deleted + */ err_t netconn_delete(struct netconn *conn) { - struct api_msg *msg; - void *mem; - + struct api_msg msg; + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ if (conn == NULL) { return ERR_OK; } - - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return ERR_MEM; - } - - msg->type = API_MSG_DELCONN; - msg->msg.conn = conn; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); - /* Drain the recvmbox. */ - if (conn->recvmbox != SYS_MBOX_NULL) { - while (sys_arch_mbox_fetch(conn->recvmbox, &mem, 1) != SYS_ARCH_TIMEOUT) { - if (conn->type == NETCONN_TCP) { - if(mem != NULL) - pbuf_free((struct pbuf *)mem); - } else { - netbuf_delete((struct netbuf *)mem); - } - } - sys_mbox_free(conn->recvmbox); - conn->recvmbox = SYS_MBOX_NULL; - } - + msg.function = do_delconn; + msg.msg.conn = conn; + tcpip_apimsg(&msg); - /* Drain the acceptmbox. */ - if (conn->acceptmbox != SYS_MBOX_NULL) { - while (sys_arch_mbox_fetch(conn->acceptmbox, &mem, 1) != SYS_ARCH_TIMEOUT) { - netconn_delete((struct netconn *)mem); - } - - sys_mbox_free(conn->acceptmbox); - conn->acceptmbox = SYS_MBOX_NULL; - } + conn->pcb.tcp = NULL; + netconn_free(conn); - sys_mbox_free(conn->mbox); - conn->mbox = SYS_MBOX_NULL; - if (conn->sem != SYS_SEM_NULL) { - sys_sem_free(conn->sem); - } - /* conn->sem = SYS_SEM_NULL;*/ - memp_free(MEMP_NETCONN, conn); return ERR_OK; } -enum netconn_type -netconn_type(struct netconn *conn) -{ - return conn->type; -} - +/** + * Get the local or remote IP address and port of a netconn. + * For RAW netconns, this returns the protocol instead of a port! + * + * @param conn the netconn to query + * @param addr a pointer to which to save the IP address + * @param port a pointer to which to save the port (or protocol for RAW) + * @param local 1 to get the local IP address, 0 to get the remote one + * @return ERR_CONN for invalid connections + * ERR_OK if the information was retrieved + */ err_t -netconn_peer(struct netconn *conn, struct ip_addr *addr, - u16_t *port) +netconn_getaddr(struct netconn *conn, struct ip_addr *addr, u16_t *port, u8_t local) { - switch (conn->type) { - case NETCONN_RAW: - /* return an error as connecting is only a helper for upper layers */ - return ERR_CONN; - case NETCONN_UDPLITE: - case NETCONN_UDPNOCHKSUM: - case NETCONN_UDP: -#if LWIP_UDP - if (conn->pcb.udp == NULL || - ((conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0)) - return ERR_CONN; - *addr = (conn->pcb.udp->remote_ip); - *port = conn->pcb.udp->remote_port; -#endif - break; - - case NETCONN_TCP: -#if LWIP_TCP - if (conn->pcb.tcp == NULL) - return ERR_CONN; - *addr = (conn->pcb.tcp->remote_ip); - *port = conn->pcb.tcp->remote_port; -#endif - break; - } - return (conn->err = ERR_OK); + struct api_msg msg; + + LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); + + msg.function = do_getaddr; + msg.msg.conn = conn; + msg.msg.msg.ad.ipaddr = addr; + msg.msg.msg.ad.port = port; + msg.msg.msg.ad.local = local; + TCPIP_APIMSG(&msg); + + return conn->err; } +/** + * Bind a netconn to a specific local IP address and port. + * Binding one netconn twice might not always be checked correctly! + * + * @param conn the netconn to bind + * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY + * to bind to all addresses) + * @param port the local port to bind the netconn to (not used for RAW) + * @return ERR_OK if bound, any other err_t on failure + */ err_t -netconn_addr(struct netconn *conn, struct ip_addr **addr, - u16_t *port) +netconn_bind(struct netconn *conn, struct ip_addr *addr, u16_t port) { - switch (conn->type) { - case NETCONN_RAW: -#if LWIP_RAW - *addr = &(conn->pcb.raw->local_ip); - *port = conn->pcb.raw->protocol; -#endif - break; - case NETCONN_UDPLITE: - case NETCONN_UDPNOCHKSUM: - case NETCONN_UDP: -#if LWIP_UDP - *addr = &(conn->pcb.udp->local_ip); - *port = conn->pcb.udp->local_port; -#endif - break; - - case NETCONN_TCP: -#if LWIP_TCP - *addr = &(conn->pcb.tcp->local_ip); - *port = conn->pcb.tcp->local_port; -#endif - break; - } - return (conn->err = ERR_OK); -} + struct api_msg msg; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); -err_t -netconn_bind(struct netconn *conn, struct ip_addr *addr, - u16_t port) -{ - struct api_msg *msg; - - if (conn == NULL) { - return ERR_VAL; - } - - if (conn->type != NETCONN_TCP && - conn->recvmbox == SYS_MBOX_NULL) { - if ((conn->recvmbox = sys_mbox_new()) == SYS_MBOX_NULL) { - return ERR_MEM; - } - } - - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return (conn->err = ERR_MEM); - } - msg->type = API_MSG_BIND; - msg->msg.conn = conn; - msg->msg.msg.bc.ipaddr = addr; - msg->msg.msg.bc.port = port; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); + msg.function = do_bind; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + TCPIP_APIMSG(&msg); return conn->err; } - +/** + * Connect a netconn to a specific remote IP address and port. + * + * @param conn the netconn to connect + * @param addr the remote IP address to connect to + * @param port the remote port to connect to (no used for RAW) + * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise + */ err_t -netconn_connect(struct netconn *conn, struct ip_addr *addr, - u16_t port) +netconn_connect(struct netconn *conn, struct ip_addr *addr, u16_t port) { - struct api_msg *msg; - - if (conn == NULL) { - return ERR_VAL; - } - + struct api_msg msg; - if (conn->recvmbox == SYS_MBOX_NULL) { - if ((conn->recvmbox = sys_mbox_new()) == SYS_MBOX_NULL) { - return ERR_MEM; - } - } - - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return ERR_MEM; - } - msg->type = API_MSG_CONNECT; - msg->msg.conn = conn; - msg->msg.msg.bc.ipaddr = addr; - msg->msg.msg.bc.port = port; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_connect; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + /* This is the only function which need to not block tcpip_thread */ + tcpip_apimsg(&msg); return conn->err; } +/** + * Disconnect a netconn from its current peer (only valid for UDP netconns). + * + * @param conn the netconn to disconnect + * @return TODO: return value is not set here... + */ err_t netconn_disconnect(struct netconn *conn) { - struct api_msg *msg; - - if (conn == NULL) { - return ERR_VAL; - } + struct api_msg msg; - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return ERR_MEM; - } - msg->type = API_MSG_DISCONNECT; - msg->msg.conn = conn; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); - return conn->err; - -} + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); -err_t -netconn_listen(struct netconn *conn) -{ - struct api_msg *msg; - - if (conn == NULL) { - return ERR_VAL; - } - - if (conn->acceptmbox == SYS_MBOX_NULL) { - conn->acceptmbox = sys_mbox_new(); - if (conn->acceptmbox == SYS_MBOX_NULL) { - return ERR_MEM; - } - } - - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return (conn->err = ERR_MEM); - } - msg->type = API_MSG_LISTEN; - msg->msg.conn = conn; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); + msg.function = do_disconnect; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); return conn->err; } +/** + * Set a TCP netconn into listen mode + * + * @param conn the tcp netconn to set to listen mode + * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1 + * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns + * don't return any error (yet?)) + */ +err_t +netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) +{ + struct api_msg msg; + + /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ + LWIP_UNUSED_ARG(backlog); + + LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_listen; + msg.msg.conn = conn; +#if TCP_LISTEN_BACKLOG + msg.msg.msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + TCPIP_APIMSG(&msg); + return conn->err; +} + +/** + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @return the newly accepted netconn or NULL on timeout + */ struct netconn * netconn_accept(struct netconn *conn) { struct netconn *newconn; - - if (conn == NULL) { - return NULL; + + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return NULL;); + LWIP_ERROR("netconn_accept: invalid acceptmbox", (conn->acceptmbox != SYS_MBOX_NULL), return NULL;); + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + newconn = NULL; + } else +#else + sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + +#if TCP_LISTEN_BACKLOG + if (newconn != NULL) { + /* Let the stack know that we have accepted the connection. */ + struct api_msg msg; + msg.function = do_recv; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); + } +#endif /* TCP_LISTEN_BACKLOG */ } - - sys_mbox_fetch(conn->acceptmbox, (void **)&newconn); - /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, 0); - + return newconn; } +/** + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @return a new netbuf containing received data or NULL on memory error or timeout + */ struct netbuf * netconn_recv(struct netconn *conn) { - struct api_msg *msg; - struct netbuf *buf; + struct api_msg msg; + struct netbuf *buf = NULL; struct pbuf *p; u16_t len; - - if (conn == NULL) { - return NULL; - } - + + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return NULL;); + if (conn->recvmbox == SYS_MBOX_NULL) { + /* @todo: should calling netconn_recv on a TCP listen conn be fatal (ERR_CONN)?? */ + /* TCP listen conns don't have a recvmbox! */ conn->err = ERR_CONN; return NULL; } - if (conn->err != ERR_OK) { + if (ERR_IS_FATAL(conn->err)) { return NULL; } if (conn->type == NETCONN_TCP) { - if (conn->pcb.tcp->state == LISTEN) { +#if LWIP_TCP + if (conn->state == NETCONN_LISTEN) { + /* @todo: should calling netconn_recv on a TCP listen conn be fatal?? */ conn->err = ERR_CONN; return NULL; } - buf = memp_malloc(MEMP_NETBUF); if (buf == NULL) { conn->err = ERR_MEM; return NULL; } - - sys_mbox_fetch(conn->recvmbox, (void **)&p); - if (p != NULL) - { - len = p->tot_len; - conn->recv_avail -= len; +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, conn->recv_timeout)==SYS_ARCH_TIMEOUT) { + memp_free(MEMP_NETBUF, buf); + conn->err = ERR_TIMEOUT; + return NULL; } - else - len = 0; - +#else + sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + + if (p != NULL) { + len = p->tot_len; + SYS_ARCH_DEC(conn->recv_avail, len); + } else { + len = 0; + } + /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, len); + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); - /* If we are closed, we indicate that we no longer wish to receive - data by setting conn->recvmbox to SYS_MBOX_NULL. */ + /* If we are closed, we indicate that we no longer wish to use the socket */ if (p == NULL) { memp_free(MEMP_NETBUF, buf); - sys_mbox_free(conn->recvmbox); - conn->recvmbox = SYS_MBOX_NULL; + /* Avoid to lose any previous error code */ + if (conn->err == ERR_OK) { + conn->err = ERR_CLSD; + } return NULL; } buf->p = p; buf->ptr = p; - buf->fromport = 0; - buf->fromaddr = NULL; + buf->port = 0; + buf->addr = NULL; /* Let the stack know that we have taken the data. */ - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - conn->err = ERR_MEM; - return buf; - } - msg->type = API_MSG_RECV; - msg->msg.conn = conn; + msg.function = do_recv; + msg.msg.conn = conn; if (buf != NULL) { - msg->msg.msg.len = buf->p->tot_len; + msg.msg.msg.r.len = buf->p->tot_len; } else { - msg->msg.msg.len = 1; + msg.msg.msg.r.len = 1; } - api_msg_post(msg); - - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); + TCPIP_APIMSG(&msg); +#endif /* LWIP_TCP */ } else { - sys_mbox_fetch(conn->recvmbox, (void **)&buf); - conn->recv_avail -= buf->p->tot_len; - /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len); +#if (LWIP_UDP || LWIP_RAW) +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, conn->recv_timeout)==SYS_ARCH_TIMEOUT) { + buf = NULL; + } +#else + sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + if (buf!=NULL) { + SYS_ARCH_DEC(conn->recv_avail, buf->p->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ } - - - LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err)); - return buf; } +/** + * Send data (in form of a netbuf) to a specific remote IP address and port. + * Only to be used for UDP and RAW netconns (not TCP). + * + * @param conn the netconn over which to send data + * @param buf a netbuf containing the data to send + * @param addr the remote IP address to which to send the data + * @param port the remote port to which to send the data + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_sendto(struct netconn *conn, struct netbuf *buf, struct ip_addr *addr, u16_t port) +{ + if (buf != NULL) { + buf->addr = addr; + buf->port = port; + return netconn_send(conn, buf); + } + return ERR_VAL; +} + +/** + * Send data over a UDP or RAW netconn (that is already connected). + * + * @param conn the UDP or RAW netconn over which to send data + * @param buf a netbuf containing the data to send + * @return ERR_OK if data was sent, any other err_t on error + */ err_t netconn_send(struct netconn *conn, struct netbuf *buf) { - struct api_msg *msg; + struct api_msg msg; + + LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;); - if (conn == NULL) { - return ERR_VAL; - } - - if (conn->err != ERR_OK) { - return conn->err; - } + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len)); + msg.function = do_send; + msg.msg.conn = conn; + msg.msg.msg.b = buf; + TCPIP_APIMSG(&msg); + return conn->err; +} - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return (conn->err = ERR_MEM); - } +/** + * Send data over a TCP netconn. + * + * @param conn the TCP netconn over which to send data + * @param dataptr pointer to the application buffer that contains the data to send + * @param size size of the application data to send + * @param apiflags combination of following flags : + * - NETCONN_COPY (0x01) data will be copied into memory belonging to the stack + * - NETCONN_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags) +{ + struct api_msg msg; - LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %d bytes\n", buf->p->tot_len)); - msg->type = API_MSG_SEND; - msg->msg.conn = conn; - msg->msg.msg.p = buf->p; - api_msg_post(msg); + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;); - sys_mbox_fetch(conn->mbox, NULL); - memp_free(MEMP_API_MSG, msg); + msg.function = do_write; + msg.msg.conn = conn; + msg.msg.msg.w.dataptr = dataptr; + msg.msg.msg.w.apiflags = apiflags; + msg.msg.msg.w.len = size; + /* For locking the core: this _can_ be delayed on low memory/low send buffer, + but if it is, this is done inside api_msg.c:do_write(), so we can use the + non-blocking version here. */ + TCPIP_APIMSG(&msg); return conn->err; } +/** + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ err_t -netconn_write(struct netconn *conn, void *dataptr, u16_t size, u8_t copy) +netconn_close(struct netconn *conn) { - struct api_msg *msg; - u16_t len; - - if (conn == NULL) { - return ERR_VAL; - } - - if (conn->err != ERR_OK) { - return conn->err; - } - - if (conn->sem == SYS_SEM_NULL) { - conn->sem = sys_sem_new(0); - if (conn->sem == SYS_SEM_NULL) { - return ERR_MEM; - } - } - - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return (conn->err = ERR_MEM); - } - msg->type = API_MSG_WRITE; - msg->msg.conn = conn; - + struct api_msg msg; - conn->state = NETCONN_WRITE; - while (conn->err == ERR_OK && size > 0) { - msg->msg.msg.w.dataptr = dataptr; - msg->msg.msg.w.copy = copy; - - if (conn->type == NETCONN_TCP) { - if (tcp_sndbuf(conn->pcb.tcp) == 0) { - sys_sem_wait(conn->sem); - if (conn->err != ERR_OK) { - goto ret; - } - } - if (size > tcp_sndbuf(conn->pcb.tcp)) { - /* We cannot send more than one send buffer's worth of data at a - time. */ - len = tcp_sndbuf(conn->pcb.tcp); - } else { - len = size; - } - } else { - len = size; - } - - LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: writing %d bytes (%d)\n", len, copy)); - msg->msg.msg.w.len = len; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - if (conn->err == ERR_OK) { - dataptr = (void *)((u8_t *)dataptr + len); - size -= len; - } else if (conn->err == ERR_MEM) { - conn->err = ERR_OK; - sys_sem_wait(conn->sem); - } else { - goto ret; - } - } - ret: - memp_free(MEMP_API_MSG, msg); - conn->state = NETCONN_NONE; - if (conn->sem != SYS_SEM_NULL) { - sys_sem_free(conn->sem); - conn->sem = SYS_SEM_NULL; - } - + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_close; + msg.msg.conn = conn; + tcpip_apimsg(&msg); return conn->err; } +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * + * @param conn the UDP netconn for which to change multicast addresses + * @param multiaddr IP address of the multicast group to join or leave + * @param interface the IP address of the network interface on which to send + * the igmp message + * @param join_or_leave flag whether to send a join- or leave-message + * @return ERR_OK if the action was taken, any err_t on error + */ err_t -netconn_close(struct netconn *conn) +netconn_join_leave_group(struct netconn *conn, + struct ip_addr *multiaddr, + struct ip_addr *interface, + enum netconn_igmp join_or_leave) { - struct api_msg *msg; + struct api_msg msg; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); - if (conn == NULL) { - return ERR_VAL; - } - if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { - return (conn->err = ERR_MEM); + msg.function = do_join_leave_group; + msg.msg.conn = conn; + msg.msg.msg.jl.multiaddr = multiaddr; + msg.msg.msg.jl.interface = interface; + msg.msg.msg.jl.join_or_leave = join_or_leave; + TCPIP_APIMSG(&msg); + return conn->err; +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Execute a DNS query, only one IP address is returned + * + * @param name a string representation of the DNS host name to query + * @param addr a preallocated struct ip_addr where to store the resolved IP address + * @return ERR_OK: resolving succeeded + * ERR_MEM: memory error, try again later + * ERR_ARG: dns client not initialized or invalid hostname + * ERR_VAL: dns server response was invalid + */ +err_t +netconn_gethostbyname(const char *name, struct ip_addr *addr) +{ + struct dns_api_msg msg; + err_t err; + sys_sem_t sem; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); + + sem = sys_sem_new(0); + if (sem == SYS_SEM_NULL) { + return ERR_MEM; } - conn->state = NETCONN_CLOSE; - again: - msg->type = API_MSG_CLOSE; - msg->msg.conn = conn; - api_msg_post(msg); - sys_mbox_fetch(conn->mbox, NULL); - if (conn->err == ERR_MEM && - conn->sem != SYS_SEM_NULL) { - sys_sem_wait(conn->sem); - goto again; - } - conn->state = NETCONN_NONE; - memp_free(MEMP_API_MSG, msg); - return conn->err; + msg.name = name; + msg.addr = addr; + msg.err = &err; + msg.sem = sem; + + tcpip_callback(do_gethostbyname, &msg); + sys_sem_wait(sem); + sys_sem_free(sem); + + return err; } +#endif /* LWIP_DNS*/ -err_t -netconn_err(struct netconn *conn) -{ - return conn->err; -} - +#endif /* LWIP_NETCONN */ diff --git a/packages/net/lwip_tcpip/current/src/api/api_msg.c b/packages/net/lwip_tcpip/current/src/api/api_msg.c --- a/packages/net/lwip_tcpip/current/src/api/api_msg.c +++ b/packages/net/lwip_tcpip/current/src/api/api_msg.c @@ -1,3 +1,9 @@ +/** + * @file + * Sequential API Internal module + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -31,177 +37,311 @@ */ #include "lwip/opt.h" -#include "lwip/arch.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + #include "lwip/api_msg.h" + +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" + #include "lwip/memp.h" -#include "lwip/sys.h" #include "lwip/tcpip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" + +#include + +/* forward declarations */ +#if LWIP_TCP +static err_t do_writemore(struct netconn *conn); +static void do_close_internal(struct netconn *conn); +#endif #if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only references it and sends it to + * conn->recvmbox + * + * @see raw.h (struct raw_pcb.recv) for parameters and return value + */ static u8_t recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr) { + struct pbuf *q; struct netbuf *buf; struct netconn *conn; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + LWIP_UNUSED_ARG(addr); conn = arg; - if (!conn) return 0; - if (conn->recvmbox != SYS_MBOX_NULL) { - if (!(buf = memp_malloc(MEMP_NETBUF))) { - return 0; +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL) && + ((recv_avail + (int)(p->tot_len)) <= conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL)) { +#endif /* LWIP_SO_RCVBUF */ + /* copy the whole packet into new pbufs */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(q != NULL) { + if (pbuf_copy(q, p) != ERR_OK) { + pbuf_free(q); + q = NULL; + } } - pbuf_ref(p); - buf->p = p; - buf->ptr = p; - buf->fromaddr = addr; - buf->fromport = pcb->protocol; + + if(q != NULL) { + buf = memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } - conn->recv_avail += p->tot_len; - /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, p->tot_len); - sys_mbox_post(conn->recvmbox, buf); + buf->p = q; + buf->ptr = q; + buf->addr = &(((struct ip_hdr*)(q->payload))->src); + buf->port = pcb->protocol; + + if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return 0; + } else { + SYS_ARCH_INC(conn->recv_avail, q->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, q->tot_len); + } + } } return 0; /* do not eat the packet */ } -#endif +#endif /* LWIP_RAW*/ + #if LWIP_UDP +/** + * Receive callback function for UDP netconns. + * Posts the packet to conn->recvmbox or deletes it on memory error. + * + * @see udp.h (struct udp_pcb.recv) for parameters + */ static void recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) { struct netbuf *buf; struct netconn *conn; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg != NULL); conn = arg; - - if (conn == NULL) { + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { +#endif /* LWIP_SO_RCVBUF */ pbuf_free(p); return; } - if (conn->recvmbox != SYS_MBOX_NULL) { - buf = memp_malloc(MEMP_NETBUF); - if (buf == NULL) { - pbuf_free(p); - return; - } else { - buf->p = p; - buf->ptr = p; - buf->fromaddr = addr; - buf->fromport = port; + + buf = memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + buf->addr = addr; + buf->port = port; +#if LWIP_NETBUF_RECVINFO + { + const struct ip_hdr* iphdr = ip_current_header(); + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); + buf->toaddr = (struct ip_addr*)&iphdr->dest; + buf->toport = udphdr->dest; } +#endif /* LWIP_NETBUF_RECVINFO */ + } - conn->recv_avail += p->tot_len; + if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } else { + SYS_ARCH_INC(conn->recv_avail, p->tot_len); /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, p->tot_len); - sys_mbox_post(conn->recvmbox, buf); + API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len); } } #endif /* LWIP_UDP */ + #if LWIP_TCP - +/** + * Receive callback function for TCP netconns. + * Posts the packet to conn->recvmbox, but doesn't delete it on errors. + * + * @see tcp.h (struct tcp_pcb.recv) for parameters and return value + */ static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct netconn *conn; u16_t len; - - conn = arg; - if (conn == NULL) { - pbuf_free(p); + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); + conn = arg; + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { return ERR_VAL; } - if (conn->recvmbox != SYS_MBOX_NULL) { - - conn->err = err; - if (p != NULL) { - len = p->tot_len; - conn->recv_avail += len; - } - else - len = 0; + conn->err = err; + if (p != NULL) { + len = p->tot_len; + SYS_ARCH_INC(conn->recv_avail, len); + } else { + len = 0; + } + + if (sys_mbox_trypost(conn->recvmbox, p) != ERR_OK) { + return ERR_MEM; + } else { /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, len); - sys_mbox_post(conn->recvmbox, p); - } + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + return ERR_OK; } - +/** + * Poll callback function for TCP netconns. + * Wakes up an application thread that waits for a connection to close + * or data to be sent. The application thread then takes the + * appropriate action to go on. + * + * Signals the conn->sem. + * netconn_close waits for conn->sem if closing failed. + * + * @see tcp.h (struct tcp_pcb.poll) for parameters and return value + */ static err_t poll_tcp(void *arg, struct tcp_pcb *pcb) { - struct netconn *conn; + struct netconn *conn = arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); - conn = arg; - if (conn != NULL && - (conn->state == NETCONN_WRITE || conn->state == NETCONN_CLOSE) && - conn->sem != SYS_SEM_NULL) { - sys_sem_signal(conn->sem); + if (conn->state == NETCONN_WRITE) { + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); } + return ERR_OK; } +/** + * Sent callback function for TCP netconns. + * Signals the conn->sem and calls API_EVENT. + * netconn_write waits for conn->sem if send buffer is low. + * + * @see tcp.h (struct tcp_pcb.sent) for parameters and return value + */ static err_t sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) { - struct netconn *conn; + struct netconn *conn = arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); - conn = arg; - if (conn != NULL && conn->sem != SYS_SEM_NULL) { - sys_sem_signal(conn->sem); + if (conn->state == NETCONN_WRITE) { + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); } - if (conn && conn->callback) - if (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) - (*conn->callback)(conn, NETCONN_EVT_SENDPLUS, len); + if (conn) { + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); + } + } return ERR_OK; } +/** + * Error callback function for TCP netconns. + * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. + * The application thread has then to decide what to do. + * + * @see tcp.h (struct tcp_pcb.err) for parameters + */ static void err_tcp(void *arg, err_t err) { struct netconn *conn; conn = arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); conn->pcb.tcp = NULL; - conn->err = err; if (conn->recvmbox != SYS_MBOX_NULL) { /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); sys_mbox_post(conn->recvmbox, NULL); } - if (conn->mbox != SYS_MBOX_NULL) { - sys_mbox_post(conn->mbox, NULL); + if (conn->op_completed != SYS_SEM_NULL && conn->state == NETCONN_CONNECT) { + conn->state = NETCONN_NONE; + sys_sem_signal(conn->op_completed); } if (conn->acceptmbox != SYS_MBOX_NULL) { - /* Register event with callback */ - if (conn->callback) - (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); sys_mbox_post(conn->acceptmbox, NULL); } - if (conn->sem != SYS_SEM_NULL) { - sys_sem_signal(conn->sem); + if ((conn->state == NETCONN_WRITE) || (conn->state == NETCONN_CLOSE)) { + /* calling do_writemore/do_close_internal is not necessary + since the pcb has already been deleted! */ + conn->state = NETCONN_NONE; + /* wake up the waiting task */ + sys_sem_signal(conn->op_completed); } } +/** + * Setup a tcp_pcb with the correct callback function pointers + * and their arguments. + * + * @param conn the TCP netconn to setup + */ static void setup_tcp(struct netconn *conn) { struct tcp_pcb *pcb; - + pcb = conn->pcb.tcp; tcp_arg(pcb, conn); tcp_recv(pcb, recv_tcp); @@ -210,623 +350,894 @@ setup_tcp(struct netconn *conn) tcp_err(pcb, err_tcp); } +/** + * Accept callback function for TCP netconns. + * Allocates a new netconn and posts that to conn->acceptmbox. + * + * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value + */ static err_t accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) { - sys_mbox_t mbox; struct netconn *newconn; struct netconn *conn; - + #if API_MSG_DEBUG #if TCP_DEBUG tcp_debug_print_state(newpcb->state); #endif /* TCP_DEBUG */ #endif /* API_MSG_DEBUG */ conn = (struct netconn *)arg; - mbox = conn->acceptmbox; - newconn = memp_malloc(MEMP_NETCONN); + + LWIP_ERROR("accept_function: invalid conn->acceptmbox", + conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;); + + /* We have to set the callback here even though + * the new socket is unknown. conn->socket is marked as -1. */ + newconn = netconn_alloc(conn->type, conn->callback); if (newconn == NULL) { return ERR_MEM; } - newconn->type = NETCONN_TCP; newconn->pcb.tcp = newpcb; setup_tcp(newconn); - newconn->recvmbox = sys_mbox_new(); - if (newconn->recvmbox == SYS_MBOX_NULL) { - memp_free(MEMP_NETCONN, newconn); + newconn->err = err; + + if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the connection is aborted in tcp_process(), + so do nothing here! */ + newconn->pcb.tcp = NULL; + netconn_free(newconn); return ERR_MEM; - } - newconn->mbox = sys_mbox_new(); - if (newconn->mbox == SYS_MBOX_NULL) { - sys_mbox_free(newconn->recvmbox); - memp_free(MEMP_NETCONN, newconn); - return ERR_MEM; + } else { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); } - newconn->sem = sys_sem_new(0); - if (newconn->sem == SYS_SEM_NULL) { - sys_mbox_free(newconn->recvmbox); - sys_mbox_free(newconn->mbox); - memp_free(MEMP_NETCONN, newconn); - return ERR_MEM; - } - newconn->acceptmbox = SYS_MBOX_NULL; - newconn->err = err; - /* Register event with callback */ - if (conn->callback) - { - (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0); - /* We have to set the callback here even though - * the new socket is unknown. Mark the socket as -1. */ - newconn->callback = conn->callback; - newconn->socket = -1; - } - - sys_mbox_post(mbox, newconn); + return ERR_OK; } #endif /* LWIP_TCP */ -static void -do_newconn(struct api_msg_msg *msg) +/** + * Create a new pcb of a specific type. + * Called from do_newconn(). + * + * @param msg the api_msg_msg describing the connection type + * @return msg->conn->err, but the return value is currently ignored + */ +static err_t +pcb_new(struct api_msg_msg *msg) { - if(msg->conn->pcb.tcp != NULL) { - /* This "new" connection already has a PCB allocated. */ - /* Is this an error condition? Should it be deleted? - We currently just are happy and return. */ - sys_mbox_post(msg->conn->mbox, NULL); - return; - } + msg->conn->err = ERR_OK; - msg->conn->err = ERR_OK; + LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); /* Allocate a PCB for this connection */ - switch(msg->conn->type) { + switch(NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW case NETCONN_RAW: -#if LWIP_RAW - msg->conn->pcb.raw = raw_new(msg->msg.bc.port); /* misusing the port field */ - raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); -#endif + msg->conn->pcb.raw = raw_new(msg->msg.n.proto); + if(msg->conn->pcb.raw == NULL) { + msg->conn->err = ERR_MEM; + break; + } + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); break; - - - case NETCONN_UDPLITE: -#if LWIP_UDP - msg->conn->pcb.udp = udp_new(); - if(msg->conn->pcb.udp == NULL) { - msg->conn->err = ERR_MEM; - break; - } - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; -#endif /* LWIP_UDP */ - case NETCONN_UDPNOCHKSUM: +#endif /* LWIP_RAW */ #if LWIP_UDP - msg->conn->pcb.udp = udp_new(); - if(msg->conn->pcb.udp == NULL) { - msg->conn->err = ERR_MEM; - break; - } - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(msg->conn->pcb.udp == NULL) { + msg->conn->err = ERR_MEM; + break; + } +#if LWIP_UDPLITE + if (msg->conn->type==NETCONN_UDPLITE) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (msg->conn->type==NETCONN_UDPNOCHKSUM) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); + break; #endif /* LWIP_UDP */ - case NETCONN_UDP: -#if LWIP_UDP - msg->conn->pcb.udp = udp_new(); - if(msg->conn->pcb.udp == NULL) { - msg->conn->err = ERR_MEM; - break; - } - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); -#endif /* LWIP_UDP */ - break; - +#if LWIP_TCP case NETCONN_TCP: -#if LWIP_TCP - msg->conn->pcb.tcp = tcp_new(); - if(msg->conn->pcb.tcp == NULL) { - msg->conn->err = ERR_MEM; - break; - } - setup_tcp(msg->conn); -#endif - break; + msg->conn->pcb.tcp = tcp_new(); + if(msg->conn->pcb.tcp == NULL) { + msg->conn->err = ERR_MEM; + break; + } + setup_tcp(msg->conn); + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->conn->err = ERR_VAL; + break; } - - - sys_mbox_post(msg->conn->mbox, NULL); + + return msg->conn->err; +} + +/** + * Create a new pcb of a specific type inside a netconn. + * Called from netconn_new_with_proto_and_callback. + * + * @param msg the api_msg_msg describing the connection type + */ +void +do_newconn(struct api_msg_msg *msg) +{ + if(msg->conn->pcb.tcp == NULL) { + pcb_new(msg); + } + /* Else? This "new" connection already has a PCB allocated. */ + /* Is this an error condition? Should it be deleted? */ + /* We currently just are happy and return. */ + + TCPIP_APIMSG_ACK(msg); } +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is NOT created! + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_alloc(enum netconn_type t, netconn_callback callback) +{ + struct netconn *conn; + int size; + conn = memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + +#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \ + (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE) + size = DEFAULT_RAW_RECVMBOX_SIZE; +#else + switch(NETCONNTYPE_GROUP(t)) { +#if LWIP_RAW + case NETCONN_RAW: + size = DEFAULT_RAW_RECVMBOX_SIZE; + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + size = DEFAULT_UDP_RECVMBOX_SIZE; + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + size = DEFAULT_TCP_RECVMBOX_SIZE; + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); + break; + } +#endif + + if ((conn->op_completed = sys_sem_new(0)) == SYS_SEM_NULL) { + memp_free(MEMP_NETCONN, conn); + return NULL; + } + if ((conn->recvmbox = sys_mbox_new(size)) == SYS_MBOX_NULL) { + sys_sem_free(conn->op_completed); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + + conn->acceptmbox = SYS_MBOX_NULL; + conn->state = NETCONN_NONE; + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; + conn->callback = callback; + conn->recv_avail = 0; +#if LWIP_TCP + conn->write_msg = NULL; + conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 0; +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_TCP */ +#if LWIP_SO_RCVTIMEO + conn->recv_timeout = 0; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; +#endif /* LWIP_SO_RCVBUF */ + return conn; +} + +/** + * Delete a netconn and all its resources. + * The pcb is NOT freed (since we might not be in the right thread context do this). + * + * @param conn the netconn to free + */ +void +netconn_free(struct netconn *conn) +{ + void *mem; + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + + /* Drain the recvmbox. */ + if (conn->recvmbox != SYS_MBOX_NULL) { + while (sys_mbox_tryfetch(conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { + if (conn->type == NETCONN_TCP) { + if(mem != NULL) { + pbuf_free((struct pbuf *)mem); + } + } else { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(conn->recvmbox); + conn->recvmbox = SYS_MBOX_NULL; + } + + /* Drain the acceptmbox. */ + if (conn->acceptmbox != SYS_MBOX_NULL) { + while (sys_mbox_tryfetch(conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + netconn_delete((struct netconn *)mem); + } + sys_mbox_free(conn->acceptmbox); + conn->acceptmbox = SYS_MBOX_NULL; + } + + sys_sem_free(conn->op_completed); + conn->op_completed = SYS_SEM_NULL; + + memp_free(MEMP_NETCONN, conn); +} + +#if LWIP_TCP +/** + * Internal helper function to close a TCP netconn: since this sometimes + * doesn't work at the first attempt, this function is called from multiple + * places. + * + * @param conn the TCP netconn to close + */ static void +do_close_internal(struct netconn *conn) +{ + err_t err; + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP)); + LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + + /* Set back some callback pointers */ + tcp_arg(conn->pcb.tcp, NULL); + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + /* some callbacks have to be reset if tcp_close is not successful */ + tcp_sent(conn->pcb.tcp, NULL); + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + /* Try to close the connection */ + err = tcp_close(conn->pcb.tcp); + if (err == ERR_OK) { + /* Closing succeeded */ + conn->state = NETCONN_NONE; + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + conn->err = ERR_OK; + /* Trigger select() in socket layer. This send should something else so the + errorfd is set, not the read and write fd! */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + /* wake up the application task */ + sys_sem_signal(conn->op_completed); + } else { + /* Closing failed, restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); + tcp_sent(conn->pcb.tcp, sent_tcp); + tcp_poll(conn->pcb.tcp, poll_tcp, 4); + tcp_err(conn->pcb.tcp, err_tcp); + tcp_arg(conn->pcb.tcp, conn); + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ +} +#endif /* LWIP_TCP */ + +/** + * Delete the pcb inside a netconn. + * Called from netconn_delete. + * + * @param msg the api_msg_msg pointing to the connection + */ +void do_delconn(struct api_msg_msg *msg) { if (msg->conn->pcb.tcp != NULL) { - switch (msg->conn->type) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: raw_remove(msg->conn->pcb.raw); break; -#endif +#endif /* LWIP_RAW */ #if LWIP_UDP - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ case NETCONN_UDP: msg->conn->pcb.udp->recv_arg = NULL; udp_remove(msg->conn->pcb.udp); break; #endif /* LWIP_UDP */ -#if LWIP_TCP +#if LWIP_TCP case NETCONN_TCP: - if (msg->conn->pcb.tcp->state == LISTEN) { - tcp_arg(msg->conn->pcb.tcp, NULL); - tcp_accept(msg->conn->pcb.tcp, NULL); - tcp_close(msg->conn->pcb.tcp); - } else { - tcp_arg(msg->conn->pcb.tcp, NULL); - tcp_sent(msg->conn->pcb.tcp, NULL); - tcp_recv(msg->conn->pcb.tcp, NULL); - tcp_poll(msg->conn->pcb.tcp, NULL, 0); - tcp_err(msg->conn->pcb.tcp, NULL); - if (tcp_close(msg->conn->pcb.tcp) != ERR_OK) { - tcp_abort(msg->conn->pcb.tcp); - } - } -#endif - default: - break; + msg->conn->state = NETCONN_CLOSE; + do_close_internal(msg->conn); + /* API_EVENT is called inside do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; +#endif /* LWIP_TCP */ + default: + break; } } - /* Trigger select() in socket layer */ - if (msg->conn->callback) - { - (*msg->conn->callback)(msg->conn, NETCONN_EVT_RCVPLUS, 0); - (*msg->conn->callback)(msg->conn, NETCONN_EVT_SENDPLUS, 0); - } - - if (msg->conn->mbox != SYS_MBOX_NULL) { - sys_mbox_post(msg->conn->mbox, NULL); + /* tcp netconns don't come here! */ + + /* Trigger select() in socket layer. This send should something else so the + errorfd is set, not the read and write fd! */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + + if (msg->conn->op_completed != SYS_SEM_NULL) { + sys_sem_signal(msg->conn->op_completed); } } -static void +/** + * Bind a pcb contained in a netconn + * Called from netconn_bind. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to bind to + */ +void do_bind(struct api_msg_msg *msg) { - if (msg->conn->pcb.tcp == NULL) { - switch (msg->conn->type) { + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW - case NETCONN_RAW: - msg->conn->pcb.raw = raw_new(msg->msg.bc.port); /* misusing the port field as protocol */ - raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); - break; -#endif + case NETCONN_RAW: + msg->conn->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ #if LWIP_UDP - case NETCONN_UDPLITE: - msg->conn->pcb.udp = udp_new(); - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; - case NETCONN_UDPNOCHKSUM: - msg->conn->pcb.udp = udp_new(); - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; - case NETCONN_UDP: - msg->conn->pcb.udp = udp_new(); - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; + case NETCONN_UDP: + msg->conn->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; #endif /* LWIP_UDP */ -#if LWIP_TCP - case NETCONN_TCP: - msg->conn->pcb.tcp = tcp_new(); - setup_tcp(msg->conn); +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; #endif /* LWIP_TCP */ - default: - break; + default: + break; + } + } else { + /* msg->conn->pcb is NULL */ + msg->conn->err = ERR_VAL; } } - switch (msg->conn->type) { -#if LWIP_RAW - case NETCONN_RAW: - msg->conn->err = raw_bind(msg->conn->pcb.raw,msg->msg.bc.ipaddr); - break; -#endif -#if LWIP_UDP - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ - case NETCONN_UDP: - msg->conn->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); - break; -#endif /* LWIP_UDP */ + TCPIP_APIMSG_ACK(msg); +} + #if LWIP_TCP - case NETCONN_TCP: - msg->conn->err = tcp_bind(msg->conn->pcb.tcp, - msg->msg.bc.ipaddr, msg->msg.bc.port); -#endif /* LWIP_TCP */ - default: - break; - } - sys_mbox_post(msg->conn->mbox, NULL); -} -#if LWIP_TCP - +/** + * TCP callback function if a connection (opened by tcp_connect/do_connect) has + * been established (or reset by the remote host). + * + * @see tcp.h (struct tcp_pcb.connected) for parameters and return values + */ static err_t do_connected(void *arg, struct tcp_pcb *pcb, err_t err) { struct netconn *conn; + LWIP_UNUSED_ARG(pcb); + conn = arg; if (conn == NULL) { return ERR_VAL; } - + conn->err = err; - if (conn->type == NETCONN_TCP && err == ERR_OK) { + if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) { setup_tcp(conn); - } - sys_mbox_post(conn->mbox, NULL); + } + conn->state = NETCONN_NONE; + sys_sem_signal(conn->op_completed); return ERR_OK; } -#endif +#endif /* LWIP_TCP */ -static void +/** + * Connect a pcb contained inside a netconn + * Called from netconn_connect. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to connect to + */ +void do_connect(struct api_msg_msg *msg) { if (msg->conn->pcb.tcp == NULL) { - switch (msg->conn->type) { -#if LWIP_RAW - case NETCONN_RAW: - msg->conn->pcb.raw = raw_new(msg->msg.bc.port); /* misusing the port field as protocol */ - raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); - break; -#endif -#if LWIP_UDP - case NETCONN_UDPLITE: - msg->conn->pcb.udp = udp_new(); - if (msg->conn->pcb.udp == NULL) { - msg->conn->err = ERR_MEM; - sys_mbox_post(msg->conn->mbox, NULL); - return; - } - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; - case NETCONN_UDPNOCHKSUM: - msg->conn->pcb.udp = udp_new(); - if (msg->conn->pcb.udp == NULL) { - msg->conn->err = ERR_MEM; - sys_mbox_post(msg->conn->mbox, NULL); - return; - } - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; - case NETCONN_UDP: - msg->conn->pcb.udp = udp_new(); - if (msg->conn->pcb.udp == NULL) { - msg->conn->err = ERR_MEM; - sys_mbox_post(msg->conn->mbox, NULL); - return; - } - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); - break; -#endif /* LWIP_UDP */ -#if LWIP_TCP - case NETCONN_TCP: - msg->conn->pcb.tcp = tcp_new(); - if (msg->conn->pcb.tcp == NULL) { - msg->conn->err = ERR_MEM; - sys_mbox_post(msg->conn->mbox, NULL); - return; - } -#endif - default: - break; - } + sys_sem_signal(msg->conn->op_completed); + return; } - switch (msg->conn->type) { + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: - raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); - sys_mbox_post(msg->conn->mbox, NULL); + msg->conn->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + sys_sem_signal(msg->conn->op_completed); break; -#endif +#endif /* LWIP_RAW */ #if LWIP_UDP - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ case NETCONN_UDP: - udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); - sys_mbox_post(msg->conn->mbox, NULL); + msg->conn->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + sys_sem_signal(msg->conn->op_completed); break; -#endif -#if LWIP_TCP +#endif /* LWIP_UDP */ +#if LWIP_TCP case NETCONN_TCP: - /* tcp_arg(msg->conn->pcb.tcp, msg->conn);*/ + msg->conn->state = NETCONN_CONNECT; setup_tcp(msg->conn); - tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port, - do_connected); - /*tcp_output(msg->conn->pcb.tcp);*/ -#endif - + msg->conn->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port, + do_connected); + /* sys_sem_signal() is called from do_connected (or err_tcp()), + * when the connection is established! */ + break; +#endif /* LWIP_TCP */ default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->conn->err = ERR_VAL; + sys_sem_signal(msg->conn->op_completed); }while(0)); break; } } -static void +/** + * Connect a pcb contained inside a netconn + * Only used for UDP netconns. + * Called from netconn_disconnect. + * + * @param msg the api_msg_msg pointing to the connection to disconnect + */ +void do_disconnect(struct api_msg_msg *msg) { - - switch (msg->conn->type) { - case NETCONN_RAW: - /* Do nothing as connecting is only a helper for upper lwip layers */ - break; - - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ - case NETCONN_UDP: #if LWIP_UDP + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { udp_disconnect(msg->conn->pcb.udp); -#endif - break; - case NETCONN_TCP: - break; } - sys_mbox_post(msg->conn->mbox, NULL); +#endif /* LWIP_UDP */ + TCPIP_APIMSG_ACK(msg); } - -static void +/** + * Set a TCP pcb contained in a netconn into listen mode + * Called from netconn_listen. + * + * @param msg the api_msg_msg pointing to the connection + */ +void do_listen(struct api_msg_msg *msg) { - if (msg->conn->pcb.tcp != NULL) { - switch (msg->conn->type) { - - case NETCONN_RAW: -#if LWIP_RAW - LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen RAW: cannot listen for RAW.\n")); -#endif - break; - - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ - case NETCONN_UDP: -#if LWIP_UDP - LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen UDP: cannot listen for UDP.\n")); -#endif /* LWIP_UDP */ - break; +#if LWIP_TCP + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { + if (msg->conn->pcb.tcp->state == CLOSED) { +#if TCP_LISTEN_BACKLOG + struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + if (lpcb == NULL) { + msg->conn->err = ERR_MEM; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (msg->conn->recvmbox != SYS_MBOX_NULL) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(msg->conn->recvmbox); + msg->conn->recvmbox = SYS_MBOX_NULL; + } + if (msg->conn->acceptmbox == SYS_MBOX_NULL) { + if ((msg->conn->acceptmbox = sys_mbox_new(DEFAULT_ACCEPTMBOX_SIZE)) == SYS_MBOX_NULL) { + msg->conn->err = ERR_MEM; + } + } + if (msg->conn->err == ERR_OK) { + msg->conn->state = NETCONN_LISTEN; + msg->conn->pcb.tcp = lpcb; + tcp_arg(msg->conn->pcb.tcp, msg->conn); + tcp_accept(msg->conn->pcb.tcp, accept_function); + } + } + } else { + msg->conn->err = ERR_CONN; + } + } + } + } +#endif /* LWIP_TCP */ + TCPIP_APIMSG_ACK(msg); +} - - case NETCONN_TCP: -#if LWIP_TCP - msg->conn->pcb.tcp = tcp_listen(msg->conn->pcb.tcp); - if (msg->conn->pcb.tcp == NULL) - { - msg->conn->err = ERR_MEM; - } - else - { - if (msg->conn->acceptmbox == SYS_MBOX_NULL) - { - msg->conn->acceptmbox = sys_mbox_new(); - if (msg->conn->acceptmbox == SYS_MBOX_NULL) - { - msg->conn->err = ERR_MEM; - break; - } - } - tcp_arg(msg->conn->pcb.tcp, msg->conn); - tcp_accept(msg->conn->pcb.tcp, accept_function); +/** + * Send some data on a RAW or UDP pcb contained in a netconn + * Called from netconn_send + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_send(struct api_msg_msg *msg) +{ + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.b->addr == NULL) { + msg->conn->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->conn->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, msg->msg.b->addr); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.b->addr == NULL) { + msg->conn->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->conn->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, msg->msg.b->addr, msg->msg.b->port); + } + break; +#endif /* LWIP_UDP */ + default: + break; } -#endif - break; + } + } + TCPIP_APIMSG_ACK(msg); +} - default: - break; +/** + * Indicate data has been received from a TCP pcb contained in a netconn + * Called from netconn_recv + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_recv(struct api_msg_msg *msg) +{ +#if LWIP_TCP + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { +#if TCP_LISTEN_BACKLOG + if (msg->conn->pcb.tcp->state == LISTEN) { + tcp_accepted(msg->conn->pcb.tcp); + } else +#endif /* TCP_LISTEN_BACKLOG */ + { + tcp_recved(msg->conn->pcb.tcp, msg->msg.r.len); + } + } } } - sys_mbox_post(msg->conn->mbox, NULL); +#endif /* LWIP_TCP */ + TCPIP_APIMSG_ACK(msg); } -static void -do_accept(struct api_msg_msg *msg) +#if LWIP_TCP +/** + * See if more data needs to be written from a previous call to netconn_write. + * Called initially from do_write. If the first call can't send all data + * (because of low memory or empty send-buffer), this function is called again + * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the + * blocking application thread (waiting in netconn_write) is released. + * + * @param conn netconn (that is currently in state NETCONN_WRITE) to process + * @return ERR_OK + * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished + */ +static err_t +do_writemore(struct netconn *conn) { - if (msg->conn->pcb.tcp != NULL) { - switch (msg->conn->type) { + err_t err; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + + dataptr = (u8_t*)conn->write_msg->msg.w.dataptr + conn->write_offset; + diff = conn->write_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; +#endif + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; +#endif + } - case NETCONN_RAW: -#if LWIP_RAW - LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept RAW: cannot accept for RAW.\n")); + err = tcp_write(conn->pcb.tcp, dataptr, len, conn->write_msg->msg.w.apiflags); + LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->write_msg->msg.w.len)); + if (err == ERR_OK) { + conn->write_offset += len; + if (conn->write_offset == conn->write_msg->msg.w.len) { + /* everything was written */ + write_finished = 1; + conn->write_msg = NULL; + conn->write_offset = 0; + /* API_EVENT might call tcp_tmr, so reset conn->state now */ + conn->state = NETCONN_NONE; + } + err = tcp_output_nagle(conn->pcb.tcp); + conn->err = err; + if ((err == ERR_OK) && (tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT)) { + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + } else if (err == ERR_MEM) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called + we do NOT return to the application thread, since ERR_MEM is + only a temporary error! */ + + /* tcp_enqueue returned ERR_MEM, try tcp_output anyway */ + err = tcp_output(conn->pcb.tcp); + +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; #endif - break; + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + conn->err = err; + write_finished = 1; + } + + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->state = NETCONN_NONE; +#if LWIP_TCPIP_CORE_LOCKING + if (conn->write_delayed != 0) +#endif + { + sys_sem_signal(conn->op_completed); + } + } +#if LWIP_TCPIP_CORE_LOCKING + else + return ERR_MEM; +#endif + return ERR_OK; +} +#endif /* LWIP_TCP */ - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ - case NETCONN_UDP: -#if LWIP_UDP - LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept UDP: cannot accept for UDP.\n")); +/** + * Send some data on a TCP pcb contained in a netconn + * Called from netconn_write + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_write(struct api_msg_msg *msg) +{ + if (!ERR_IS_FATAL(msg->conn->err)) { + if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { +#if LWIP_TCP + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by do_writemore */ + LWIP_ASSERT("already writing", msg->conn->write_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->write_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + msg->conn->write_delayed = 0; + if (do_writemore(msg->conn) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(msg->conn->op_completed, 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else + do_writemore(msg->conn); +#endif + /* for both cases: if do_writemore was called, don't ACK the APIMSG! */ + return; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->conn->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Return a connection's local or remote address + * Called from netconn_getaddr + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_getaddr(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.ip != NULL) { + *(msg->msg.ad.ipaddr) = (msg->msg.ad.local?msg->conn->pcb.ip->local_ip:msg->conn->pcb.ip->remote_ip); + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; + } else { + /* return an error as connecting is only a helper for upper layers */ + msg->conn->err = ERR_CONN; + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; + } else { + if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { + msg->conn->err = ERR_CONN; + } else { + *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; #endif /* LWIP_UDP */ - break; +#if LWIP_TCP case NETCONN_TCP: + *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port); break; +#endif /* LWIP_TCP */ } + } else { + msg->conn->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Close a TCP pcb contained in a netconn + * Called from netconn_close + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_close(struct api_msg_msg *msg) +{ +#if LWIP_TCP + if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + msg->conn->state = NETCONN_CLOSE; + do_close_internal(msg->conn); + /* for tcp netconns, do_close_internal ACKs the message */ + } else +#endif /* LWIP_TCP */ + { + msg->conn->err = ERR_VAL; + sys_sem_signal(msg->conn->op_completed); } } -static void -do_send(struct api_msg_msg *msg) -{ - if (msg->conn->pcb.tcp != NULL) { - switch (msg->conn->type) { - - case NETCONN_RAW: -#if LWIP_RAW - raw_send(msg->conn->pcb.raw, msg->msg.p); -#endif - break; - - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ - case NETCONN_UDP: +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_join_leave_group(struct api_msg_msg *msg) +{ + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { #if LWIP_UDP - udp_send(msg->conn->pcb.udp, msg->msg.p); + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->conn->err = igmp_joingroup(msg->msg.jl.interface, msg->msg.jl.multiaddr); + } else { + msg->conn->err = igmp_leavegroup(msg->msg.jl.interface, msg->msg.jl.multiaddr); + } #endif /* LWIP_UDP */ - break; - - case NETCONN_TCP: - break; +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->conn->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } } } - sys_mbox_post(msg->conn->mbox, NULL); + TCPIP_APIMSG_ACK(msg); } +#endif /* LWIP_IGMP */ +#if LWIP_DNS +/** + * Callback function that is called when DNS name is resolved + * (or on timeout). A waiting application thread is waked up by + * signaling the semaphore. + */ static void -do_recv(struct api_msg_msg *msg) +do_dns_found(const char *name, struct ip_addr *ipaddr, void *arg) { -#if LWIP_TCP - if (msg->conn->pcb.tcp != NULL) { - if (msg->conn->type == NETCONN_TCP) { - tcp_recved(msg->conn->pcb.tcp, msg->msg.len); - } + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0); + + if (ipaddr == NULL) { + /* timeout or memory error */ + *msg->err = ERR_VAL; + } else { + /* address was resolved */ + *msg->err = ERR_OK; + *msg->addr = *ipaddr; } -#endif - sys_mbox_post(msg->conn->mbox, NULL); + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); } -static void -do_write(struct api_msg_msg *msg) +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +do_gethostbyname(void *arg) { -#if LWIP_TCP - err_t err; -#endif - if (msg->conn->pcb.tcp != NULL) { - switch (msg->conn->type) { - - case NETCONN_RAW: -#if LWIP_RAW - msg->conn->err = ERR_VAL; -#endif - break; - - - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ - case NETCONN_UDP: -#if LWIP_UDP - msg->conn->err = ERR_VAL; -#endif /* LWIP_UDP */ - break; - - case NETCONN_TCP: -#if LWIP_TCP - err = tcp_write(msg->conn->pcb.tcp, msg->msg.w.dataptr, - msg->msg.w.len, msg->msg.w.copy); - /* This is the Nagle algorithm: inhibit the sending of new TCP - segments when new outgoing data arrives from the user if any - previously transmitted data on the connection remains - unacknowledged. */ - if(err == ERR_OK && (msg->conn->pcb.tcp->unacked == NULL || (msg->conn->pcb.tcp->flags & TF_NODELAY)) ) { - tcp_output(msg->conn->pcb.tcp); - } - msg->conn->err = err; - if (msg->conn->callback) - if (err == ERR_OK) - { - if (tcp_sndbuf(msg->conn->pcb.tcp) <= TCP_SNDLOWAT) - (*msg->conn->callback)(msg->conn, NETCONN_EVT_SENDMINUS, msg->msg.w.len); - } -#endif - break; - - - default: - break; - } - } - sys_mbox_post(msg->conn->mbox, NULL); -} - -static void -do_close(struct api_msg_msg *msg) -{ - err_t err; + struct dns_api_msg *msg = (struct dns_api_msg*)arg; - err = ERR_OK; - - if (msg->conn->pcb.tcp != NULL) { - switch (msg->conn->type) { - - case NETCONN_RAW: - break; - - - case NETCONN_UDPLITE: - /* FALLTHROUGH */ - case NETCONN_UDPNOCHKSUM: - /* FALLTHROUGH */ - case NETCONN_UDP: - break; - - - case NETCONN_TCP: -#if LWIP_TCP - if (msg->conn->pcb.tcp->state == LISTEN) { - err = tcp_close(msg->conn->pcb.tcp); - } - msg->conn->err = err; -#endif - break; - - default: - break; - } + *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg); + if (*msg->err != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); } - sys_mbox_post(msg->conn->mbox, NULL); } +#endif /* LWIP_DNS */ -typedef void (* api_msg_decode)(struct api_msg_msg *msg); -static api_msg_decode decode[API_MSG_MAX] = { - do_newconn, - do_delconn, - do_bind, - do_connect, - do_disconnect, - do_listen, - do_accept, - do_send, - do_recv, - do_write, - do_close - }; -void -api_msg_input(struct api_msg *msg) -{ - decode[msg->type](&(msg->msg)); -} - -void -api_msg_post(struct api_msg *msg) -{ - tcpip_apimsg(msg); -} - - - +#endif /* LWIP_NETCONN */ diff --git a/packages/net/lwip_tcpip/current/src/api/err.c b/packages/net/lwip_tcpip/current/src/api/err.c --- a/packages/net/lwip_tcpip/current/src/api/err.c +++ b/packages/net/lwip_tcpip/current/src/api/err.c @@ -1,3 +1,9 @@ +/** + * @file + * Error Management module + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -34,26 +40,35 @@ #ifdef LWIP_DEBUG -static char *err_strerr[] = {"Ok.", - "Out of memory error.", - "Buffer error.", - "Connection aborted.", - "Connection reset.", - "Connection closed.", - "Not connected.", - "Illegal value.", - "Illegal argument.", - "Routing problem.", - "Address in use." +static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ + "Timeout.", /* ERR_TIMEOUT -3 */ + "Routing problem.", /* ERR_RTE -4 */ + "Connection aborted.", /* ERR_ABRT -5 */ + "Connection reset.", /* ERR_RST -6 */ + "Connection closed.", /* ERR_CLSD -7 */ + "Not connected.", /* ERR_CONN -8 */ + "Illegal value.", /* ERR_VAL -9 */ + "Illegal argument.", /* ERR_ARG -10 */ + "Address in use.", /* ERR_USE -11 */ + "Low-level netif error.", /* ERR_IF -12 */ + "Already connected.", /* ERR_ISCONN -13 */ + "Operation in progress." /* ERR_INPROGRESS -14 */ }; - -char * +/** + * Convert an lwip internal error to a string representation. + * + * @param err an lwip internal err_t + * @return a string representation for err + */ +const char * lwip_strerr(err_t err) { return err_strerr[-err]; } - #endif /* LWIP_DEBUG */ diff --git a/packages/net/lwip_tcpip/current/src/api/netbuf.c b/packages/net/lwip_tcpip/current/src/api/netbuf.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/api/netbuf.c @@ -0,0 +1,240 @@ +/** + * @file + * Network buffer management + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/memp.h" + +#include + +/** + * Create (allocate) and initialize a new netbuf. + * The netbuf doesn't yet contain a packet buffer! + * + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + buf->p = NULL; + buf->ptr = NULL; + buf->addr = NULL; + buf->port = 0; +#if LWIP_NETBUF_RECVINFO + buf->toaddr = NULL; + buf->toport = 0; +#endif /* LWIP_NETBUF_RECVINFO */ + return buf; + } else { + return NULL; + } +} + +/** + * Deallocate a netbuf allocated by netbuf_new(). + * + * @param buf pointer to a netbuf allocated by netbuf_new() + */ +void +netbuf_delete(struct netbuf *buf) +{ + if (buf != NULL) { + if (buf->p != NULL) { + pbuf_free(buf->p); + buf->p = buf->ptr = NULL; + } + memp_free(MEMP_NETBUF, buf); + } +} + +/** + * Allocate memory for a packet buffer for a given netbuf. + * + * @param buf the netbuf for which to allocate a packet buffer + * @param size the size of the packet buffer to allocate + * @return pointer to the allocated memory + * NULL if no memory could be allocated + */ +void * +netbuf_alloc(struct netbuf *buf, u16_t size) +{ + LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;); + + /* Deallocate any previously allocated memory. */ + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (buf->p == NULL) { + return NULL; + } + LWIP_ASSERT("check that first pbuf can hold size", + (buf->p->len >= size)); + buf->ptr = buf->p; + return buf->p->payload; +} + +/** + * Free the packet buffer included in a netbuf + * + * @param buf pointer to the netbuf which contains the packet buffer to free + */ +void +netbuf_free(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = buf->ptr = NULL; +} + +/** + * Let a netbuf reference existing (non-volatile) data. + * + * @param buf netbuf which should reference the data + * @param dataptr pointer to the data to reference + * @param size size of the data + * @return ERR_OK if data is referenced + * ERR_MEM if data couldn't be referenced due to lack of memory + */ +err_t +netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size) +{ + LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (buf->p == NULL) { + buf->ptr = NULL; + return ERR_MEM; + } + buf->p->payload = (void*)dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; + return ERR_OK; +} + +/** + * Chain one netbuf to another (@see pbuf_chain) + * + * @param head the first netbuf + * @param tail netbuf to chain after head, freed by this function, may not be reference after returning + */ +void +netbuf_chain(struct netbuf *head, struct netbuf *tail) +{ + LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;); + LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;); + pbuf_cat(head->p, tail->p); + head->ptr = head->p; + memp_free(MEMP_NETBUF, tail); +} + +/** + * Get the data pointer and length of the data inside a netbuf. + * + * @param buf netbuf to get the data from + * @param dataptr pointer to a void pointer where to store the data pointer + * @param len pointer to an u16_t where the length of the data is stored + * @return ERR_OK if the information was retreived, + * ERR_BUF on error. + */ +err_t +netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) +{ + LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;); + + if (buf->ptr == NULL) { + return ERR_BUF; + } + *dataptr = buf->ptr->payload; + *len = buf->ptr->len; + return ERR_OK; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the next part. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + * @return -1 if there is no next part + * 1 if moved to the next part but now there is no next part + * 0 if moved to the next part and there are still more parts + */ +s8_t +netbuf_next(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;); + if (buf->ptr->next == NULL) { + return -1; + } + buf->ptr = buf->ptr->next; + if (buf->ptr->next == NULL) { + return 1; + } + return 0; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the beginning of the packet. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + */ +void +netbuf_first(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + buf->ptr = buf->p; +} + +#endif /* LWIP_NETCONN */ diff --git a/packages/net/lwip_tcpip/current/src/api/netdb.c b/packages/net/lwip_tcpip/current/src/api/netdb.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/api/netdb.c @@ -0,0 +1,346 @@ +/** + * @file + * API functions for name resolving + * + */ + +/* + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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: Simon Goldschmidt + * + */ + +#include "lwip/netdb.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/err.h" +#include "lwip/mem.h" +#include "lwip/ip_addr.h" +#include "lwip/api.h" + +#include +#include + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + struct ip_addr *addrs; + struct ip_addr addr; + char *aliases; +}; + +/** h_errno is exported in netdb.h for access by applications. */ +#if LWIP_DNS_API_DECLARE_H_ERRNO +int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */ + +/** define "hostent" variables storage: 0 if we use a static (but unprotected) + * set of variables for lwip_gethostbyname, 1 if we use a local storage */ +#ifndef LWIP_DNS_API_HOSTENT_STORAGE +#define LWIP_DNS_API_HOSTENT_STORAGE 0 +#endif + +/** define "hostent" variables storage */ +#if LWIP_DNS_API_HOSTENT_STORAGE +#define HOSTENT_STORAGE +#else +#define HOSTENT_STORAGE static +#endif /* LWIP_DNS_API_STATIC_HOSTENT */ + +/** + * Returns an entry containing addresses of address family AF_INET + * for the host with name name. + * Due to dns_gethostbyname limitations, only one address is returned. + * + * @param name the hostname to resolve + * @return an entry containing addresses of address family AF_INET + * for the host with name name + */ +struct hostent* +lwip_gethostbyname(const char *name) +{ + err_t err; + struct ip_addr addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE struct ip_addr s_hostent_addr; + HOSTENT_STORAGE struct ip_addr *s_phostent_addr; + + /* query host IP address */ + err = netconn_gethostbyname(name, &addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + h_errno = HOST_NOT_FOUND; + return NULL; + } + + /* fill hostent */ + s_hostent_addr = addr; + s_phostent_addr = &s_hostent_addr; + s_hostent.h_name = (char*)name; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(struct ip_addr); + s_hostent.h_addr_list = (char**)&s_phostent_addr; + +#if DNS_DEBUG + /* dump hostent */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases)); + if (s_hostent.h_aliases != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_aliases[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx])); + } + } + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list)); + if (s_hostent.h_addr_list != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_addr_list[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa(s_hostent.h_addr_list[idx]))); + } + } +#endif /* DNS_DEBUG */ + +#if LWIP_DNS_API_HOSTENT_STORAGE + /* this function should return the "per-thread" hostent after copy from s_hostent */ + return sys_thread_hostent(&s_hostent); +#else + return &s_hostent; +#endif /* LWIP_DNS_API_HOSTENT_STORAGE */ +} + +/** + * Thread-safe variant of lwip_gethostbyname: instead of using a static + * buffer, this function takes buffer and errno pointers as arguments + * and uses these for the result. + * + * @param name the hostname to resolve + * @param ret pre-allocated struct where to store the result + * @param buf pre-allocated buffer where to store additional data + * @param buflen the size of buf + * @param result pointer to a hostent pointer that is set to ret on success + * and set to zero on error + * @param h_errnop pointer to an int where to store errors (instead of modifying + * the global h_errno) + * @return 0 on success, non-zero on error, additional error information + * is stored in *h_errnop instead of h_errno to be thread-safe + */ +int +lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop) +{ + err_t err; + struct gethostbyname_r_helper *h; + char *hostname; + size_t namelen; + int lh_errno; + + if (h_errnop == NULL) { + /* ensure h_errnop is never NULL */ + h_errnop = &lh_errno; + } + + if (result == NULL) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + /* first thing to do: set *result to nothing */ + *result = NULL; + if ((name == NULL) || (ret == NULL) || (buf == 0)) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + + namelen = strlen(name); + if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) { + /* buf can't hold the data needed + a copy of name */ + *h_errnop = ERANGE; + return -1; + } + + h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf); + hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); + + /* query host IP address */ + err = netconn_gethostbyname(name, &(h->addr)); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + *h_errnop = ENSRNOTFOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addrs = &(h->addr); + h->aliases = NULL; + ret->h_name = (char*)hostname; + ret->h_aliases = &(h->aliases); + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(struct ip_addr); + ret->h_addr_list = (char**)&(h->addrs); + + /* set result != NULL */ + *result = ret; + + /* return success */ + return 0; +} + +/** + * Frees one or more addrinfo structures returned by getaddrinfo(), along with + * any additional storage associated with those structures. If the ai_next field + * of the structure is not null, the entire list of structures is freed. + * + * @param ai struct addrinfo to free + */ +void +lwip_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + next = ai->ai_next; + mem_free(ai); + ai = next; + } +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * Due to a limitation in dns_gethostbyname, only the first address of a + * host is returned. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + struct ip_addr addr; + struct addrinfo *ai; + struct sockaddr_in *sa = NULL; + int port_nr = 0; + size_t total_size; + size_t namelen = 0; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported! */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + err = netconn_gethostbyname(nodename, &addr); + if (err != ERR_OK) { + return EAI_FAIL; + } + } else { + /* service location specified, use loopback address */ + addr.addr = htonl(INADDR_LOOPBACK); + } + + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in); + if (nodename != NULL) { + namelen = strlen(nodename); + LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); + total_size += namelen + 1; + } + ai = mem_malloc(total_size); + if (ai == NULL) { + goto memerr; + } + memset(ai, 0, total_size); + sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo)); + /* set up sockaddr */ + sa->sin_addr.s_addr = addr.addr; + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = htons(port_nr); + + /* set up addrinfo */ + ai->ai_family = AF_INET; + if (hints != NULL) { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + MEMCPY(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + + return 0; +memerr: + if (ai != NULL) { + mem_free(ai); + } + return EAI_MEMORY; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/packages/net/lwip_tcpip/current/src/api/netifapi.c b/packages/net/lwip_tcpip/current/src/api/netifapi.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/api/netifapi.c @@ -0,0 +1,162 @@ +/** + * @file + * Network Interface Sequential API module + * + */ + +/* + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netifapi.h" +#include "lwip/tcpip.h" + +/** + * Call netif_add() inside the tcpip_thread context. + */ +void +do_netifapi_netif_add( struct netifapi_msg_msg *msg) +{ + if (!netif_add( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw, + msg->msg.add.state, + msg->msg.add.init, + msg->msg.add.input)) { + msg->err = ERR_IF; + } else { + msg->err = ERR_OK; + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_set_addr() inside the tcpip_thread context. + */ +void +do_netifapi_netif_set_addr( struct netifapi_msg_msg *msg) +{ + netif_set_addr( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw); + msg->err = ERR_OK; + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the + * tcpip_thread context. + */ +void +do_netifapi_netif_common( struct netifapi_msg_msg *msg) +{ + if (msg->msg.common.errtfunc!=NULL) { + msg->err = + msg->msg.common.errtfunc(msg->netif); + } else { + msg->err = ERR_OK; + msg->msg.common.voidfunc(msg->netif); + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_add() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_add() + */ +err_t +netifapi_netif_add(struct netif *netif, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_add; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + msg.msg.msg.add.state = state; + msg.msg.msg.add.init = init; + msg.msg.msg.add.input = input; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * Call netif_set_addr() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_set_addr() + */ +err_t +netifapi_netif_set_addr(struct netif *netif, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_set_addr; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe + * way by running that function inside the tcpip_thread context. + * + * @note use only for functions where there is only "netif" parameter. + */ +err_t +netifapi_netif_common( struct netif *netif, + void (* voidfunc)(struct netif *netif), + err_t (* errtfunc)(struct netif *netif) ) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_common; + msg.msg.netif = netif; + msg.msg.msg.common.voidfunc = voidfunc; + msg.msg.msg.common.errtfunc = errtfunc; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/packages/net/lwip_tcpip/current/src/api/sockets.c b/packages/net/lwip_tcpip/current/src/api/sockets.c --- a/packages/net/lwip_tcpip/current/src/api/sockets.c +++ b/packages/net/lwip_tcpip/current/src/api/sockets.c @@ -1,3 +1,9 @@ +/** + * @file + * Sockets BSD-Like API module + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -32,86 +38,158 @@ * */ -#include -#include +#include "lwip/opt.h" -#include "lwip/opt.h" -#include "lwip/api.h" -#include "lwip/arch.h" -#include "lwip/sys.h" +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ #include "lwip/sockets.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" + +#include #define NUM_SOCKETS MEMP_NUM_NETCONN +/** Contains all internal pointers and states used for a socket */ struct lwip_socket { + /** sockets currently are built on netconns, each socket has one netconn */ struct netconn *conn; + /** data that was left from the previous read */ struct netbuf *lastdata; + /** offset in the data that was left from the previous read */ u16_t lastoffset; - u16_t rcvevent; + /** number of times data was received, set by event_callback(), + tested by the receive and select functions */ + s16_t rcvevent; + /** number of times data was received, set by event_callback(), + tested by select */ u16_t sendevent; - u16_t flags; + /** socket flags (currently, only used for O_NONBLOCK) */ + u16_t flags; + /** last error that occurred on this socket */ int err; }; -struct lwip_select_cb -{ - struct lwip_select_cb *next; - fd_set *readset; - fd_set *writeset; - fd_set *exceptset; - int sem_signalled; - sys_sem_t sem; +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** readset passed to select */ + fd_set *readset; + /** writeset passed to select */ + fd_set *writeset; + /** unimplemented: exceptset passed to select */ + fd_set *exceptset; + /** don't signal the same semaphore twice: set to 1 when signalled */ + int sem_signalled; + /** semaphore to wake up a task waiting for select */ + sys_sem_t sem; }; -static struct lwip_socket sockets[NUM_SOCKETS]; -static struct lwip_select_cb *select_cb_list = 0; - -static sys_sem_t socksem = 0; -static sys_sem_t selectsem = 0; - -static void -event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +/** This struct is used to pass data to the set/getsockopt_internal + * functions running in tcpip_thread context (only a void* is allowed) */ +struct lwip_setgetsockopt_data { + /** socket struct for which to change options */ + struct lwip_socket *sock; + /** socket index for which to change options */ + int s; + /** level of the option to process */ + int level; + /** name of the option to process */ + int optname; + /** set: value to set the option to + * get: value of the option is stored here */ + void *optval; + /** size of *optval */ + socklen_t *optlen; + /** if an error occures, it is temporarily stored here */ + err_t err; +}; -static int err_to_errno_table[11] = { - 0, /* ERR_OK 0 No error, everything OK. */ - ENOMEM, /* ERR_MEM -1 Out of memory error. */ - ENOBUFS, /* ERR_BUF -2 Buffer error. */ - ECONNABORTED, /* ERR_ABRT -3 Connection aborted. */ - ECONNRESET, /* ERR_RST -4 Connection reset. */ - ESHUTDOWN, /* ERR_CLSD -5 Connection closed. */ - ENOTCONN, /* ERR_CONN -6 Not connected. */ - EINVAL, /* ERR_VAL -7 Illegal value. */ - EIO, /* ERR_ARG -8 Illegal argument. */ - EHOSTUNREACH, /* ERR_RTE -9 Routing problem. */ - EADDRINUSE /* ERR_USE -10 Address in use. */ +/** The global array of available sockets */ +static struct lwip_socket sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; + +/** Semaphore protecting the sockets array */ +static sys_sem_t socksem; +/** Semaphore protecting select_cb_list */ +static sys_sem_t selectsem; + +/** Table to quickly map an lwIP error (err_t) to a socket error + * by using -err as an index */ +static const int err_to_errno_table[] = { + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ + ETIMEDOUT, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + ECONNABORTED, /* ERR_ABRT -5 Connection aborted. */ + ECONNRESET, /* ERR_RST -6 Connection reset. */ + ESHUTDOWN, /* ERR_CLSD -7 Connection closed. */ + ENOTCONN, /* ERR_CONN -8 Not connected. */ + EINVAL, /* ERR_VAL -9 Illegal value. */ + EIO, /* ERR_ARG -10 Illegal argument. */ + EADDRINUSE, /* ERR_USE -11 Address in use. */ + -1, /* ERR_IF -12 Low-level netif error */ + -1, /* ERR_ISCONN -13 Already connected. */ + EINPROGRESS /* ERR_INPROGRESS -14 Operation in progress */ }; #define ERR_TO_ERRNO_TABLE_SIZE \ (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0])) #define err_to_errno(err) \ - (-(err) >= 0 && -(err) < ERR_TO_ERRNO_TABLE_SIZE ? \ + ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \ err_to_errno_table[-(err)] : EIO) #ifdef ERRNO +#ifndef set_errno #define set_errno(err) errno = (err) +#endif #else #define set_errno(err) #endif #define sock_set_errno(sk, e) do { \ - sk->err = (e); \ - set_errno(sk->err); \ + sk->err = (e); \ + set_errno(sk->err); \ } while (0) +/* Forward delcaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +static void lwip_getsockopt_internal(void *arg); +static void lwip_setsockopt_internal(void *arg); +/** + * Initialize this module. This function has to be called before any other + * functions in this module! + */ +void +lwip_socket_init(void) +{ + socksem = sys_sem_new(1); + selectsem = sys_sem_new(1); +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_socket for the socket or NULL if not found + */ static struct lwip_socket * get_socket(int s) { struct lwip_socket *sock; - if ((s < 0) || (s > NUM_SOCKETS)) { + if ((s < 0) || (s >= NUM_SOCKETS)) { LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); set_errno(EBADF); return NULL; @@ -128,27 +206,30 @@ get_socket(int s) return sock; } +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @return the index of the new socket; -1 on error + */ static int alloc_socket(struct netconn *newconn) { int i; - if (!socksem) - socksem = sys_sem_new(1); - /* Protect socket array */ sys_sem_wait(socksem); /* allocate a new socket identifier */ - for(i = 0; i < NUM_SOCKETS; ++i) { + for (i = 0; i < NUM_SOCKETS; ++i) { if (!sockets[i].conn) { - sockets[i].conn = newconn; - sockets[i].lastdata = NULL; + sockets[i].conn = newconn; + sockets[i].lastdata = NULL; sockets[i].lastoffset = 0; - sockets[i].rcvevent = 0; - sockets[i].sendevent = 1; /* TCP send buf is empty */ - sockets[i].flags = 0; - sockets[i].err = 0; + sockets[i].rcvevent = 0; + sockets[i].sendevent = 1; /* TCP send buf is empty */ + sockets[i].flags = 0; + sockets[i].err = 0; sys_sem_signal(socksem); return i; } @@ -157,63 +238,97 @@ alloc_socket(struct netconn *newconn) return -1; } +/* Below this, the well-known socket functions are implemented. + * Use google.com or opengroup.org to get a good description :-) + * + * Exceptions are documented! + */ + int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { - struct lwip_socket *sock; + struct lwip_socket *sock, *nsock; struct netconn *newconn; struct ip_addr naddr; u16_t port; int newsock; struct sockaddr_in sin; + err_t err; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) + return -1; + + if ((sock->flags & O_NONBLOCK) && (sock->rcvevent <= 0)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); return -1; } newconn = netconn_accept(sock->conn); + if (!newconn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) failed, err=%d\n", s, sock->conn->err)); + sock_set_errno(sock, err_to_errno(sock->conn->err)); + return -1; + } /* get the IP address and port of the remote host */ - netconn_peer(newconn, &naddr, &port); + err = netconn_peer(newconn, &naddr, &port); + if (err != ERR_OK) { + netconn_delete(newconn); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - sin.sin_addr.s_addr = naddr.addr; + /* Note that POSIX only requires us to check addr is non-NULL. addrlen must + * not be NULL if addr is valid. + */ + if (NULL != addr) { + LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = naddr.addr; - if (*addrlen > sizeof(sin)) + if (*addrlen > sizeof(sin)) *addrlen = sizeof(sin); - memcpy(addr, &sin, *addrlen); + MEMCPY(addr, &sin, *addrlen); + } newsock = alloc_socket(newconn); if (newsock == -1) { netconn_delete(newconn); - sock_set_errno(sock, ENOBUFS); - return -1; + sock_set_errno(sock, ENFILE); + return -1; } + LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); newconn->callback = event_callback; - sock = get_socket(newsock); + nsock = &sockets[newsock]; + LWIP_ASSERT("invalid socket pointer", nsock != NULL); sys_sem_wait(socksem); - sock->rcvevent += -1 - newconn->socket; + /* See event_callback: If data comes in right away after an accept, even + * though the server task might not have created a new socket yet. + * In that case, newconn->socket is counted down (newconn->socket--), + * so nsock->rcvevent is >= 1 here! + */ + nsock->rcvevent += -1 - newconn->socket; newconn->socket = newsock; sys_sem_signal(socksem); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); ip_addr_debug_print(SOCKETS_DEBUG, &naddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", port)); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); sock_set_errno(sock, 0); return newsock; } int -lwip_bind(int s, struct sockaddr *name, socklen_t namelen) +lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) { struct lwip_socket *sock; struct ip_addr local_addr; @@ -221,17 +336,19 @@ lwip_bind(int s, struct sockaddr *name, err_t err; sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) return -1; - } - local_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; - local_port = ((struct sockaddr_in *)name)->sin_port; + LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((((const struct sockaddr_in *)name)->sin_family) == AF_INET)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + local_addr.addr = ((const struct sockaddr_in *)name)->sin_addr.s_addr; + local_port = ((const struct sockaddr_in *)name)->sin_port; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); ip_addr_debug_print(SOCKETS_DEBUG, &local_addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(local_port))); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port))); err = netconn_bind(sock->conn, &local_addr, ntohs(local_port)); @@ -252,59 +369,56 @@ lwip_close(int s) struct lwip_socket *sock; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); - if (!socksem) - socksem = sys_sem_new(1); - - /* We cannot allow multiple closes of the same socket. */ - sys_sem_wait(socksem); sock = get_socket(s); if (!sock) { - sys_sem_signal(socksem); - set_errno(EBADF); - return -1; + return -1; } netconn_delete(sock->conn); + + sys_sem_wait(socksem); if (sock->lastdata) { netbuf_delete(sock->lastdata); } - sock->lastdata = NULL; + sock->lastdata = NULL; sock->lastoffset = 0; - sock->conn = NULL; + sock->conn = NULL; + sock_set_errno(sock, 0); sys_sem_signal(socksem); - sock_set_errno(sock, 0); return 0; } int -lwip_connect(int s, struct sockaddr *name, socklen_t namelen) +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) { struct lwip_socket *sock; err_t err; sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) return -1; - } - if (((struct sockaddr_in *)name)->sin_family == AF_UNSPEC) { + LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((((const struct sockaddr_in *)name)->sin_family) == AF_INET)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + if (((const struct sockaddr_in *)name)->sin_family == AF_UNSPEC) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); err = netconn_disconnect(sock->conn); } else { struct ip_addr remote_addr; u16_t remote_port; - remote_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; - remote_port = ((struct sockaddr_in *)name)->sin_port; + remote_addr.addr = ((const struct sockaddr_in *)name)->sin_addr.s_addr; + remote_port = ((const struct sockaddr_in *)name)->sin_port; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(remote_port))); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port))); err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); - } + } if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); @@ -317,6 +431,14 @@ lwip_connect(int s, struct sockaddr *nam return 0; } +/** + * Set a socket into listen mode. + * The socket may not have been used for another connection previously. + * + * @param s the socket to set to listening mode + * @param backlog (ATTENTION: need TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ int lwip_listen(int s, int backlog) { @@ -324,13 +446,20 @@ lwip_listen(int s, int backlog) err_t err; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); + sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) return -1; + + /* limit the "backlog" parameter to fit in an u8_t */ + if (backlog < 0) { + backlog = 0; + } + if (backlog > 0xff) { + backlog = 0xff; } - err = netconn_listen(sock->conn); + err = netconn_listen_with_backlog(sock->conn, backlog); if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); @@ -343,225 +472,298 @@ lwip_listen(int s, int backlog) } int -lwip_recvfrom(int s, void *mem, int len, unsigned int flags, +lwip_recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) { struct lwip_socket *sock; - struct netbuf *buf; - u16_t buflen, copylen; - struct ip_addr *addr; - u16_t port; + struct netbuf *buf; + u16_t buflen, copylen, off = 0; + struct ip_addr *addr; + u16_t port; + u8_t done = 0; - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %d, 0x%x, ..)\n", s, mem, len, flags)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags)); sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) return -1; - } + + do { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", (void*)sock->lastdata)); + /* Check if there is data left from the last recv operation. */ + if (sock->lastdata) { + buf = sock->lastdata; + } else { + /* If this is non-blocking call, then check first */ + if (((flags & MSG_DONTWAIT) || (sock->flags & O_NONBLOCK)) && + (sock->rcvevent <= 0)) { + if (off > 0) { + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* No data was left from the previous operation, so we try to get + some from the network. */ + sock->lastdata = buf = netconn_recv(sock->conn); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv netbuf=%p\n", (void*)buf)); - /* Check if there is data left from the last recv operation. */ - if (sock->lastdata) { - buf = sock->lastdata; - } else { - /* If this is non-blocking call, then check first */ - if (((flags & MSG_DONTWAIT) || (sock->flags & O_NONBLOCK)) - && !sock->rcvevent) - { - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); - sock_set_errno(sock, EWOULDBLOCK); - return -1; + if (!buf) { + if (off > 0) { + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + /* We should really do some error checking here. */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL!\n", s)); + sock_set_errno(sock, (((sock->conn->pcb.ip != NULL) && (sock->conn->err == ERR_OK)) + ? ETIMEDOUT : err_to_errno(sock->conn->err))); + return 0; + } + } + + buflen = netbuf_len(buf); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%"U16_F" sock->lastoffset=%"U16_F"\n", + buflen, len, off, sock->lastoffset)); + + buflen -= sock->lastoffset; + + if (len > buflen) { + copylen = buflen; + } else { + copylen = (u16_t)len; } - /* No data was left from the previous operation, so we try to get - some from the network. */ - buf = netconn_recv(sock->conn); + /* copy the contents of the received buffer into + the supplied memory pointer mem */ + netbuf_copy_partial(buf, (u8_t*)mem + off, copylen, sock->lastoffset); + + off += copylen; - if (!buf) { - /* We should really do some error checking here. */ - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL!\n", s)); - sock_set_errno(sock, 0); - return 0; + if (netconn_type(sock->conn) == NETCONN_TCP) { + LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen); + len -= copylen; + if ( (len <= 0) || + (buf->p->flags & PBUF_FLAG_PUSH) || + (sock->rcvevent <= 0) || + ((flags & MSG_PEEK)!=0)) { + done = 1; + } + } else { + done = 1; } - } - - buflen = netbuf_len(buf); - - buflen -= sock->lastoffset; - if (len > buflen) { - copylen = buflen; - } else { - copylen = len; - } + /* Check to see from where the data was.*/ + if (done) { + if (from && fromlen) { + struct sockaddr_in sin; - /* copy the contents of the received buffer into - the supplied memory pointer mem */ - netbuf_copy_partial(buf, mem, copylen, sock->lastoffset); + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = (struct ip_addr*)&(sin.sin_addr.s_addr); + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + } - /* Check to see from where the data was. */ - if (from && fromlen) { - struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = addr->addr; - addr = netbuf_fromaddr(buf); - port = netbuf_fromport(buf); + if (*fromlen > sizeof(sin)) { + *fromlen = sizeof(sin); + } - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - sin.sin_addr.s_addr = addr->addr; - - if (*fromlen > sizeof(sin)) - *fromlen = sizeof(sin); + MEMCPY(from, &sin, *fromlen); - memcpy(from, &sin, *fromlen); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%"U16_F"\n", port, off)); + } else { + #if SOCKETS_DEBUG + struct sockaddr_in sin; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, copylen)); - } else { -#if SOCKETS_DEBUG - addr = netbuf_fromaddr(buf); - port = netbuf_fromport(buf); + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = (struct ip_addr*)&(sin.sin_addr.s_addr); + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + } - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, copylen)); -#endif - - } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%"U16_F"\n", port, off)); + #endif /* SOCKETS_DEBUG */ + } + } - /* If this is a TCP socket, check if there is data left in the - buffer. If so, it should be saved in the sock structure for next - time around. */ - if (netconn_type(sock->conn) == NETCONN_TCP && buflen - copylen > 0) { - sock->lastdata = buf; - sock->lastoffset += copylen; - } else { - sock->lastdata = NULL; - sock->lastoffset = 0; - netbuf_delete(buf); - } - + /* If we don't peek the incoming message... */ + if ((flags & MSG_PEEK)==0) { + /* If this is a TCP socket, check if there is data left in the + buffer. If so, it should be saved in the sock structure for next + time around. */ + if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) { + sock->lastdata = buf; + sock->lastoffset += copylen; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", (void*)buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", (void*)buf)); + netbuf_delete(buf); + } + } + } while (!done); sock_set_errno(sock, 0); - return copylen; + return off; } int -lwip_read(int s, void *mem, int len) +lwip_read(int s, void *mem, size_t len) { return lwip_recvfrom(s, mem, len, 0, NULL, NULL); } int -lwip_recv(int s, void *mem, int len, unsigned int flags) +lwip_recv(int s, void *mem, size_t len, int flags) { return lwip_recvfrom(s, mem, len, flags, NULL, NULL); } int -lwip_send(int s, void *data, int size, unsigned int flags) +lwip_send(int s, const void *data, size_t size, int flags) { struct lwip_socket *sock; err_t err; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%d, flags=0x%x)\n", s, data, size, flags)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n", + s, data, size, flags)); sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) return -1; + + if (sock->conn->type != NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + return lwip_sendto(s, data, size, flags, NULL, 0); +#else + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ } - switch (netconn_type(sock->conn)) { -#if LWIP_RAW - case NETCONN_RAW: -#endif -#if LWIP_UDP - case NETCONN_UDP: - case NETCONN_UDPLITE: - case NETCONN_UDPNOCHKSUM: - { - struct netbuf *buf; - /* create a buffer */ - buf = netbuf_new(); - - if (!buf) { - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) ENOBUFS\n", s)); - sock_set_errno(sock, ENOBUFS); - return -1; - } - - /* make the buffer point to the data that should - be sent */ - netbuf_ref(buf, data, size); + err = netconn_write(sock->conn, data, size, NETCONN_COPY | ((flags & MSG_MORE)?NETCONN_MORE:0)); - /* send the data */ - err = netconn_send(sock->conn, buf); - - /* deallocated the buffer */ - netbuf_delete(buf); - } - break; -#endif -#if LWIP_TCP - case NETCONN_TCP: - err = netconn_write(sock->conn, data, size, NETCONN_COPY); - break; -#endif - default: - err = ERR_ARG; - break; - } - if (err != ERR_OK) { - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d\n", s, err)); - sock_set_errno(sock, err_to_errno(err)); - return -1; - } - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) ok size=%d\n", s, size)); - sock_set_errno(sock, 0); - return size; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%"SZT_F"\n", s, err, size)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)size : -1); } int -lwip_sendto(int s, void *data, int size, unsigned int flags, - struct sockaddr *to, socklen_t tolen) +lwip_sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen) { struct lwip_socket *sock; - struct ip_addr remote_addr, addr; - u16_t remote_port, port; - int ret,connected; + struct ip_addr remote_addr; + err_t err; + u16_t short_size; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; + u16_t remote_port; +#endif sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) return -1; + + if (sock->conn->type == NETCONN_TCP) { +#if LWIP_TCP + return lwip_send(s, data, size, flags); +#else + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ } - /* get the peer if currently connected */ - connected = (netconn_peer(sock->conn, &addr, &port) == ERR_OK); - - remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; - remote_port = ((struct sockaddr_in *)to)->sin_port; - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, size=%d, flags=0x%x to=", s, data, size, flags)); - ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", ntohs(remote_port))); + LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff); + short_size = (u16_t)size; + LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || + ((tolen == sizeof(struct sockaddr_in)) && + ((((const struct sockaddr_in *)to)->sin_family) == AF_INET))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); - - ret = lwip_send(s, data, size, flags); +#if LWIP_TCPIP_CORE_LOCKING + /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */ + { struct pbuf* p; + + p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (p == NULL) { + err = ERR_MEM; + } else { + p->payload = (void*)data; + p->len = p->tot_len = short_size; + + remote_addr.addr = ((const struct sockaddr_in *)to)->sin_addr.s_addr; + + LOCK_TCPIP_CORE(); + if (sock->conn->type==NETCONN_RAW) { + err = sock->conn->err = raw_sendto(sock->conn->pcb.raw, p, &remote_addr); + } else { + err = sock->conn->err = udp_sendto(sock->conn->pcb.udp, p, &remote_addr, ntohs(((const struct sockaddr_in *)to)->sin_port)); + } + UNLOCK_TCPIP_CORE(); + + pbuf_free(p); + } + } +#else + /* initialize a buffer */ + buf.p = buf.ptr = NULL; + if (to) { + remote_addr.addr = ((const struct sockaddr_in *)to)->sin_addr.s_addr; + remote_port = ntohs(((const struct sockaddr_in *)to)->sin_port); + buf.addr = &remote_addr; + buf.port = remote_port; + } else { + remote_addr.addr = 0; + remote_port = 0; + buf.addr = NULL; + buf.port = 0; + } - /* reset the remote address and port number - of the connection */ - if (connected) - netconn_connect(sock->conn, &addr, port); - else - netconn_disconnect(sock->conn); - return ret; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%d"U16_F", flags=0x%x to=", + s, data, short_size, flags)); + ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); + + /* make the buffer point to the data that should be sent */ +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Allocate a new netbuf and copy the data into it. */ + if (netbuf_alloc(&buf, short_size) == NULL) { + err = ERR_MEM; + } else { + err = netbuf_take(&buf, data, short_size); + } +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + err = netbuf_ref(&buf, data, short_size); +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { + /* send the data */ + err = netconn_send(sock->conn, &buf); + } + + /* deallocated the buffer */ + netbuf_free(&buf); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? short_size : -1); } int @@ -570,28 +772,29 @@ lwip_socket(int domain, int type, int pr struct netconn *conn; int i; + LWIP_UNUSED_ARG(domain); + /* create a netconn */ switch (type) { -#if LWIP_RAW case SOCK_RAW: - conn = netconn_new_with_proto_and_callback(NETCONN_RAW, protocol, event_callback); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; -#endif -#if LWIP_UDP case SOCK_DGRAM: - conn = netconn_new_with_callback(NETCONN_UDP, event_callback); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ? + NETCONN_UDPLITE : NETCONN_UDP, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; -#endif -#if LWIP_TCP case SOCK_STREAM: conn = netconn_new_with_callback(NETCONN_TCP, event_callback); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; -#endif default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", domain, type, protocol)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", + domain, type, protocol)); set_errno(EINVAL); return -1; } @@ -606,8 +809,8 @@ lwip_socket(int domain, int type, int pr if (i == -1) { netconn_delete(conn); - set_errno(ENOBUFS); - return -1; + set_errno(ENFILE); + return -1; } conn->socket = i; LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); @@ -616,481 +819,579 @@ lwip_socket(int domain, int type, int pr } int -lwip_write(int s, void *data, int size) +lwip_write(int s, const void *data, size_t size) { - return lwip_send(s, data, size, 0); + return lwip_send(s, data, size, 0); } - +/** + * Go through the readset and writeset lists and see which socket of the sockets + * set in the sets has events. On return, readset, writeset and exceptset have + * the sockets enabled that had events. + * + * exceptset is not used for now!!! + * + * @param maxfdp1 the highest socket index in the sets + * @param readset in: set of sockets to check for read events; + * out: set of sockets that had read events + * @param writeset in: set of sockets to check for write events; + * out: set of sockets that had write events + * @param exceptset not yet implemented + * @return number of sockets that had events (read+write) + */ static int lwip_selscan(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset) { - int i, nready = 0; - fd_set lreadset, lwriteset, lexceptset; - struct lwip_socket *p_sock; - - FD_ZERO(&lreadset); - FD_ZERO(&lwriteset); - FD_ZERO(&lexceptset); - - /* Go through each socket in each list to count number of sockets which - currently match */ - for(i = 0; i < maxfdp1; i++) - { - if (FD_ISSET(i, readset)) - { - /* See if netconn of this socket is ready for read */ - p_sock = get_socket(i); - if (p_sock && (p_sock->lastdata || p_sock->rcvevent)) - { - FD_SET(i, &lreadset); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); - nready++; - } - } - if (FD_ISSET(i, writeset)) - { - /* See if netconn of this socket is ready for write */ - p_sock = get_socket(i); - if (p_sock && p_sock->sendevent) - { - FD_SET(i, &lwriteset); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); - nready++; - } - } + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_socket *p_sock; + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + for(i = 0; i < maxfdp1; i++) { + if (FD_ISSET(i, readset)) { + /* See if netconn of this socket is ready for read */ + p_sock = get_socket(i); + if (p_sock && (p_sock->lastdata || (p_sock->rcvevent > 0))) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } } - *readset = lreadset; - *writeset = lwriteset; - FD_ZERO(exceptset); - - return nready; + if (FD_ISSET(i, writeset)) { + /* See if netconn of this socket is ready for write */ + p_sock = get_socket(i); + if (p_sock && p_sock->sendevent) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + } + } + *readset = lreadset; + *writeset = lwriteset; + FD_ZERO(exceptset); + + return nready; } - +/** + * Processing exceptset is not yet implemented. + */ int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout) { - int i; - int nready; - fd_set lreadset, lwriteset, lexceptset; - u32_t msectimeout; - struct lwip_select_cb select_cb; - struct lwip_select_cb *p_selcb; - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%ld tvusec=%ld)\n", maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, timeout ? timeout->tv_sec : -1L, timeout ? timeout->tv_usec : -1L)); - - select_cb.next = 0; - select_cb.readset = readset; - select_cb.writeset = writeset; - select_cb.exceptset = exceptset; - select_cb.sem_signalled = 0; + int i; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + struct lwip_select_cb *p_selcb; - /* Protect ourselves searching through the list */ - if (!selectsem) - selectsem = sys_sem_new(1); - sys_sem_wait(selectsem); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%ld tvusec=%ld)\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? (long)timeout->tv_sec : (long)-1, + timeout ? (long)timeout->tv_usec : (long)-1)); - if (readset) - lreadset = *readset; - else - FD_ZERO(&lreadset); - if (writeset) - lwriteset = *writeset; - else - FD_ZERO(&lwriteset); - if (exceptset) - lexceptset = *exceptset; - else - FD_ZERO(&lexceptset); + select_cb.next = 0; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; - /* Go through each socket in each list to count number of sockets which - currently match */ - nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); - - /* If we don't have any current events, then suspend if we are supposed to */ - if (!nready) - { - if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) - { - sys_sem_signal(selectsem); - if (readset) - FD_ZERO(readset); - if (writeset) - FD_ZERO(writeset); - if (exceptset) - FD_ZERO(exceptset); + /* Protect ourselves searching through the list */ + sys_sem_wait(selectsem); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); - set_errno(0); - - return 0; - } + if (readset) + lreadset = *readset; + else + FD_ZERO(&lreadset); + if (writeset) + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); + if (exceptset) + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); - /* add our semaphore to list */ - /* We don't actually need any dynamic memory. Our entry on the - * list is only valid while we are in this function, so it's ok - * to use local variables */ - - select_cb.sem = sys_sem_new(0); - /* Note that we are still protected */ - /* Put this select_cb on top of list */ - select_cb.next = select_cb_list; - select_cb_list = &select_cb; - - /* Now we can safely unprotect */ - sys_sem_signal(selectsem); + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); - /* Now just wait to be woken */ - if (timeout == 0) - /* Wait forever */ - msectimeout = 0; - else - msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); - - i = sys_sem_wait_timeout(select_cb.sem, msectimeout); - - /* Take us off the list */ - sys_sem_wait(selectsem); - if (select_cb_list == &select_cb) - select_cb_list = select_cb.next; - else - for (p_selcb = select_cb_list; p_selcb; p_selcb = p_selcb->next) - if (p_selcb->next == &select_cb) - { - p_selcb->next = select_cb.next; - break; - } - - sys_sem_signal(selectsem); - - sys_sem_free(select_cb.sem); - if (i == 0) /* Timeout */ - { - if (readset) - FD_ZERO(readset); - if (writeset) - FD_ZERO(writeset); - if (exceptset) - FD_ZERO(exceptset); - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); - set_errno(0); - - return 0; + /* If we don't have any current events, then suspend if we are supposed to */ + if (!nready) { + if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + sys_sem_signal(selectsem); + if (readset) + FD_ZERO(readset); + if (writeset) + FD_ZERO(writeset); + if (exceptset) + FD_ZERO(exceptset); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + set_errno(0); + + return 0; + } + + /* add our semaphore to list */ + /* We don't actually need any dynamic memory. Our entry on the + * list is only valid while we are in this function, so it's ok + * to use local variables */ + + select_cb.sem = sys_sem_new(0); + /* Note that we are still protected */ + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + select_cb_list = &select_cb; + + /* Now we can safely unprotect */ + sys_sem_signal(selectsem); + + /* Now just wait to be woken */ + if (timeout == 0) + /* Wait forever */ + msectimeout = 0; + else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if(msectimeout == 0) + msectimeout = 1; + } + + i = sys_sem_wait_timeout(select_cb.sem, msectimeout); + + /* Take us off the list */ + sys_sem_wait(selectsem); + if (select_cb_list == &select_cb) + select_cb_list = select_cb.next; + else + for (p_selcb = select_cb_list; p_selcb; p_selcb = p_selcb->next) { + if (p_selcb->next == &select_cb) { + p_selcb->next = select_cb.next; + break; } - - if (readset) - lreadset = *readset; - else - FD_ZERO(&lreadset); - if (writeset) - lwriteset = *writeset; - else - FD_ZERO(&lwriteset); - if (exceptset) - lexceptset = *exceptset; - else - FD_ZERO(&lexceptset); - - /* See what's set */ - nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); + } + + sys_sem_signal(selectsem); + + sys_sem_free(select_cb.sem); + if (i == 0) { + /* Timeout */ + if (readset) + FD_ZERO(readset); + if (writeset) + FD_ZERO(writeset); + if (exceptset) + FD_ZERO(exceptset); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + set_errno(0); + + return 0; } + + if (readset) + lreadset = *readset; else - sys_sem_signal(selectsem); - - if (readset) - *readset = lreadset; + FD_ZERO(&lreadset); if (writeset) - *writeset = lwriteset; + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); if (exceptset) - *exceptset = lexceptset; - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); - set_errno(0); - - return nready; + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); + + /* See what's set */ + nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); + } else + sys_sem_signal(selectsem); + + if (readset) + *readset = lreadset; + if (writeset) + *writeset = lwriteset; + if (exceptset) + *exceptset = lexceptset; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); + set_errno(0); + + return nready; } - +/** + * Callback registered in the netconn layer for each socket-netconn. + * Processes recvevent (data available) and wakes up tasks waiting for select. + */ static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) { - int s; - struct lwip_socket *sock; - struct lwip_select_cb *scb; + int s; + struct lwip_socket *sock; + struct lwip_select_cb *scb; - /* Get socket */ - if (conn) - { - s = conn->socket; - if (s < 0) - { - /* Data comes in right away after an accept, even though - * the server task might not have created a new socket yet. - * Just count down (or up) if that's the case and we - * will use the data later. Note that only receive events - * can happen before the new socket is set up. */ - if (evt == NETCONN_EVT_RCVPLUS) - conn->socket--; - return; - } - - sock = get_socket(s); - if (!sock) - return; - } - else - return; - - if (!selectsem) - selectsem = sys_sem_new(1); + LWIP_UNUSED_ARG(len); - sys_sem_wait(selectsem); - /* Set event as required */ - switch (evt) - { - case NETCONN_EVT_RCVPLUS: - sock->rcvevent++; - break; - case NETCONN_EVT_RCVMINUS: - sock->rcvevent--; - break; - case NETCONN_EVT_SENDPLUS: - sock->sendevent = 1; - break; - case NETCONN_EVT_SENDMINUS: - sock->sendevent = 0; - break; - } - sys_sem_signal(selectsem); - - /* Now decide if anyone is waiting for this socket */ - /* NOTE: This code is written this way to protect the select link list - but to avoid a deadlock situation by releasing socksem before - signalling for the select. This means we need to go through the list - multiple times ONLY IF a select was actually waiting. We go through - the list the number of waiting select calls + 1. This list is - expected to be small. */ - while (1) - { - sys_sem_wait(selectsem); - for (scb = select_cb_list; scb; scb = scb->next) - { - if (scb->sem_signalled == 0) - { - /* Test this select call for our socket */ - if (scb->readset && FD_ISSET(s, scb->readset)) - if (sock->rcvevent) - break; - if (scb->writeset && FD_ISSET(s, scb->writeset)) - if (sock->sendevent) - break; - } + /* Get socket */ + if (conn) { + s = conn->socket; + if (s < 0) { + /* Data comes in right away after an accept, even though + * the server task might not have created a new socket yet. + * Just count down (or up) if that's the case and we + * will use the data later. Note that only receive events + * can happen before the new socket is set up. */ + sys_sem_wait(socksem); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; } - if (scb) - { - scb->sem_signalled = 1; - sys_sem_signal(selectsem); - sys_sem_signal(scb->sem); - } else { - sys_sem_signal(selectsem); - break; - } + sys_sem_signal(socksem); + return; + } + s = conn->socket; + sys_sem_signal(socksem); } + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + sys_sem_wait(selectsem); + /* Set event as required */ + switch (evt) { + case NETCONN_EVT_RCVPLUS: + sock->rcvevent++; + break; + case NETCONN_EVT_RCVMINUS: + sock->rcvevent--; + break; + case NETCONN_EVT_SENDPLUS: + sock->sendevent = 1; + break; + case NETCONN_EVT_SENDMINUS: + sock->sendevent = 0; + break; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + sys_sem_signal(selectsem); + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code is written this way to protect the select link list + but to avoid a deadlock situation by releasing socksem before + signalling for the select. This means we need to go through the list + multiple times ONLY IF a select was actually waiting. We go through + the list the number of waiting select calls + 1. This list is + expected to be small. */ + while (1) { + sys_sem_wait(selectsem); + for (scb = select_cb_list; scb; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* Test this select call for our socket */ + if (scb->readset && FD_ISSET(s, scb->readset)) + if (sock->rcvevent > 0) + break; + if (scb->writeset && FD_ISSET(s, scb->writeset)) + if (sock->sendevent) + break; + } + } + if (scb) { + scb->sem_signalled = 1; + sys_sem_signal(scb->sem); + sys_sem_signal(selectsem); + } else { + sys_sem_signal(selectsem); + break; + } + } } - - - -int lwip_shutdown(int s, int how) +/** + * Unimplemented: Close one end of a full-duplex connection. + * Currently, the full connection is closed. + */ +int +lwip_shutdown(int s, int how) { + LWIP_UNUSED_ARG(how); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); return lwip_close(s); /* XXX temporary hack until proper implementation */ } -int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen) +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) { struct lwip_socket *sock; struct sockaddr_in sin; struct ip_addr naddr; sock = get_socket(s); - if (!sock) { - set_errno(EBADF); + if (!sock) return -1; - } - - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - - /* get the IP address and port of the remote host */ - netconn_peer(sock->conn, &naddr, &sin.sin_port); - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getpeername(%d, addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, &naddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%d)\n", sin.sin_port)); - - sin.sin_port = htons(sin.sin_port); - sin.sin_addr.s_addr = naddr.addr; - - if (*namelen > sizeof(sin)) - *namelen = sizeof(sin); - - memcpy(name, &sin, *namelen); - sock_set_errno(sock, 0); - return 0; -} - -int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen) -{ - struct lwip_socket *sock; - struct sockaddr_in sin; - struct ip_addr *naddr; - - sock = get_socket(s); - if (!sock) { - set_errno(EBADF); - return -1; - } memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; - /* get the IP address and port of the remote host */ - netconn_addr(sock->conn, &naddr, &sin.sin_port); + /* get the IP address and port */ + netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockname(%d, addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, naddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%d)\n", sin.sin_port)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin.sin_port)); sin.sin_port = htons(sin.sin_port); - sin.sin_addr.s_addr = naddr->addr; + sin.sin_addr.s_addr = naddr.addr; if (*namelen > sizeof(sin)) - *namelen = sizeof(sin); + *namelen = sizeof(sin); - memcpy(name, &sin, *namelen); + MEMCPY(name, &sin, *namelen); sock_set_errno(sock, 0); return 0; } -int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen) +int +lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) { - int err = 0; - struct lwip_socket *sock = get_socket(s); + return lwip_getaddrname(s, name, namelen, 0); +} + +int +lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 1); +} - if(!sock) { - set_errno(EBADF); +int +lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + err_t err = ERR_OK; + struct lwip_socket *sock = get_socket(s); + struct lwip_setgetsockopt_data data; + + if (!sock) return -1; - } - if( NULL == optval || NULL == optlen ) { - sock_set_errno( sock, EFAULT ); + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); return -1; } /* Do length and type checks for the various options first, to keep it readable. */ - switch( level ) { + switch (level) { /* Level: SOL_SOCKET */ case SOL_SOCKET: - switch(optname) { - - case SO_ACCEPTCONN: - case SO_BROADCAST: - /* UNIMPL case SO_DEBUG: */ - /* UNIMPL case SO_DONTROUTE: */ - case SO_ERROR: - case SO_KEEPALIVE: - /* UNIMPL case SO_OOBINLINE: */ - /* UNIMPL case SO_RCVBUF: */ - /* UNIMPL case SO_SNDBUF: */ - /* UNIMPL case SO_RCVLOWAT: */ - /* UNIMPL case SO_SNDLOWAT: */ + switch (optname) { + + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_ERROR: + case SO_KEEPALIVE: + /* UNIMPL case SO_CONTIMEO: */ + /* UNIMPL case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ #if SO_REUSE - case SO_REUSEADDR: - case SO_REUSEPORT: + case SO_REUSEADDR: + case SO_REUSEPORT: #endif /* SO_REUSE */ - case SO_TYPE: - /* UNIMPL case SO_USELOOPBACK: */ - if( *optlen < sizeof(int) ) { - err = EINVAL; - } - break; + case SO_TYPE: + /* UNIMPL case SO_USELOOPBACK: */ + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", s, optname)); - err = ENOPROTOOPT; - } /* switch */ + case SO_NO_CHECK: + if (*optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; /* Level: IPPROTO_IP */ case IPPROTO_IP: - switch(optname) { - /* UNIMPL case IP_HDRINCL: */ - /* UNIMPL case IP_RCVDSTADDR: */ - /* UNIMPL case IP_RCVIF: */ - case IP_TTL: - case IP_TOS: - if( *optlen < sizeof(int) ) { - err = EINVAL; - } - break; + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + break; + case IP_MULTICAST_IF: + if (*optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + break; +#endif /* LWIP_IGMP */ - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", s, optname)); - err = ENOPROTOOPT; - } /* switch */ - break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#if LWIP_TCP /* Level: IPPROTO_TCP */ case IPPROTO_TCP: - if( *optlen < sizeof(int) ) { - err = EINVAL; - break; + if (*optlen < sizeof(int)) { + err = EINVAL; + break; } - - /* If this is no TCP socket, ignore any options. */ - if ( sock->conn->type != NETCONN_TCP ) return 0; + + /* If this is no TCP socket, ignore any options. */ + if (sock->conn->type != NETCONN_TCP) + return 0; - switch( optname ) { - case TCP_NODELAY: - case TCP_KEEPALIVE: - break; - - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname)); - err = ENOPROTOOPT; - } /* switch */ + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) + return 0; + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE*/ /* UNDEFINED LEVEL */ default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", s, level, optname)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); err = ENOPROTOOPT; } /* switch */ - if( 0 != err ) { + if (err != ERR_OK) { sock_set_errno(sock, err); return -1; } - - /* Now do the actual option processing */ + data.sock = sock; + data.level = level; + data.optname = optname; + data.optval = optval; + data.optlen = optlen; + data.err = err; + tcpip_callback(lwip_getsockopt_internal, &data); + sys_arch_sem_wait(sock->conn->op_completed, 0); + /* maybe lwip_getsockopt_internal has changed err */ + err = data.err; - switch(level) { + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_getsockopt_internal(void *arg) +{ + struct lwip_socket *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { /* Level: SOL_SOCKET */ case SOL_SOCKET: - switch( optname ) { + switch (optname) { /* The option flags */ case SO_ACCEPTCONN: @@ -1104,107 +1405,180 @@ int lwip_getsockopt (int s, int level, i case SO_REUSEPORT: #endif /* SO_REUSE */ /*case SO_USELOOPBACK: UNIMPL */ - *(int*)optval = sock->conn->pcb.tcp->so_options & optname; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", s, optname, (*(int*)optval?"on":"off"))); + *(int*)optval = sock->conn->pcb.ip->so_options & optname; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", + s, optname, (*(int*)optval?"on":"off"))); break; case SO_TYPE: - switch (sock->conn->type) { -#if LWIP_RAW + switch (NETCONNTYPE_GROUP(sock->conn->type)) { case NETCONN_RAW: *(int*)optval = SOCK_RAW; break; -#endif -#if LWIP_TCP case NETCONN_TCP: *(int*)optval = SOCK_STREAM; break; -#endif -#if LWIP_UDP case NETCONN_UDP: - case NETCONN_UDPLITE: - case NETCONN_UDPNOCHKSUM: *(int*)optval = SOCK_DGRAM; break; -#endif default: /* unrecognized socket type */ *(int*)optval = sock->conn->type; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", s, *(int *)optval)); - } /* switch */ - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", s, *(int *)optval)); + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (sock->conn->type) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); break; case SO_ERROR: + if (sock->err == 0) { + sock_set_errno(sock, err_to_errno(sock->conn->err)); + } *(int *)optval = sock->err; sock->err = 0; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", s, *(int *)optval)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + *(int *)optval = sock->conn->recv_timeout; break; - } /* switch */ +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + *(int *)optval = sock->conn->recv_bufsize; + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + } /* switch (optname) */ break; /* Level: IPPROTO_IP */ case IPPROTO_IP: - switch( optname ) { + switch (optname) { case IP_TTL: - *(int*)optval = sock->conn->pcb.tcp->ttl; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", s, *(int *)optval)); + *(int*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", + s, *(int *)optval)); break; case IP_TOS: - *(int*)optval = sock->conn->pcb.tcp->tos; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", s, *(int *)optval)); + *(int*)optval = sock->conn->pcb.ip->tos; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", + s, *(int *)optval)); break; - } /* switch */ +#if LWIP_IGMP + case IP_MULTICAST_TTL: + *(u8_t*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_MULTICAST_IF: + ((struct in_addr*) optval)->s_addr = sock->conn->pcb.udp->multicast_ip.addr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", + s, *(u32_t *)optval)); + break; +#endif /* LWIP_IGMP */ + } /* switch (optname) */ break; +#if LWIP_TCP /* Level: IPPROTO_TCP */ case IPPROTO_TCP: - switch( optname ) { + switch (optname) { case TCP_NODELAY: - *(int*)optval = (sock->conn->pcb.tcp->flags & TF_NODELAY); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", s, (*(int*)optval)?"on":"off") ); + *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", + s, (*(int*)optval)?"on":"off") ); break; case TCP_KEEPALIVE: - *(int*)optval = (int)sock->conn->pcb.tcp->keepalive; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", s, *(int *)optval)); + *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", + s, *(int *)optval)); break; - } /* switch */ + case TCP_KEEPINTVL: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPCNT: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + + } /* switch (optname) */ break; - } - - - sock_set_errno(sock, err); - return err ? -1 : 0; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + } /* switch (level) */ + sys_sem_signal(sock->conn->op_completed); } -int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen) +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) { struct lwip_socket *sock = get_socket(s); - int err = 0; + int err = ERR_OK; + struct lwip_setgetsockopt_data data; - if(!sock) { - set_errno(EBADF); + if (!sock) + return -1; + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); return -1; } - if( NULL == optval ) { - sock_set_errno( sock, EFAULT ); - return -1; - } - - /* Do length and type checks for the various options first, to keep it readable. */ - switch( level ) { + switch (level) { /* Level: SOL_SOCKET */ case SOL_SOCKET: - switch(optname) { + switch (optname) { case SO_BROADCAST: /* UNIMPL case SO_DEBUG: */ /* UNIMPL case SO_DONTROUTE: */ case SO_KEEPALIVE: + /* UNIMPL case case SO_CONTIMEO: */ + /* UNIMPL case case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ /* UNIMPL case SO_OOBINLINE: */ - /* UNIMPL case SO_RCVBUF: */ /* UNIMPL case SO_SNDBUF: */ /* UNIMPL case SO_RCVLOWAT: */ /* UNIMPL case SO_SNDLOWAT: */ @@ -1213,76 +1587,185 @@ int lwip_setsockopt (int s, int level, i case SO_REUSEPORT: #endif /* SO_REUSE */ /* UNIMPL case SO_USELOOPBACK: */ - if( optlen < sizeof(int) ) { + if (optlen < sizeof(int)) { err = EINVAL; } break; + case SO_NO_CHECK: + if (optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", s, optname)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); err = ENOPROTOOPT; - } /* switch */ + } /* switch (optname) */ break; /* Level: IPPROTO_IP */ case IPPROTO_IP: - switch(optname) { + switch (optname) { /* UNIMPL case IP_HDRINCL: */ /* UNIMPL case IP_RCVDSTADDR: */ /* UNIMPL case IP_RCVIF: */ case IP_TTL: case IP_TOS: - if( optlen < sizeof(int) ) { + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_IF: + if (optlen < sizeof(struct in_addr)) { err = EINVAL; } - break; + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + if (optlen < sizeof(struct ip_mreq)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", s, optname)); - err = ENOPROTOOPT; - } /* switch */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ break; +#if LWIP_TCP /* Level: IPPROTO_TCP */ case IPPROTO_TCP: - if( optlen < sizeof(int) ) { + if (optlen < sizeof(int)) { err = EINVAL; - break; + break; } /* If this is no TCP socket, ignore any options. */ - if ( sock->conn->type != NETCONN_TCP ) return 0; + if (sock->conn->type != NETCONN_TCP) + return 0; - switch( optname ) { + switch (optname) { case TCP_NODELAY: case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ break; default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); err = ENOPROTOOPT; - } /* switch */ + } /* switch (optname) */ break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) + return 0; -/* UNDEFINED LEVEL */ + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE */ +/* UNDEFINED LEVEL */ default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", s, level, optname)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); err = ENOPROTOOPT; - } /* switch */ + } /* switch (level) */ - if( 0 != err ) { + if (err != ERR_OK) { sock_set_errno(sock, err); return -1; } + /* Now do the actual option processing */ + data.sock = sock; + data.level = level; + data.optname = optname; + data.optval = (void*)optval; + data.optlen = &optlen; + data.err = err; + tcpip_callback(lwip_setsockopt_internal, &data); + sys_arch_sem_wait(sock->conn->op_completed, 0); + /* maybe lwip_setsockopt_internal has changed err */ + err = data.err; - /* Now do the actual option processing */ + sock_set_errno(sock, err); + return err ? -1 : 0; +} - switch(level) { +static void +lwip_setsockopt_internal(void *arg) +{ + struct lwip_socket *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + const void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { /* Level: SOL_SOCKET */ case SOL_SOCKET: - switch(optname) { + switch (optname) { /* The option flags */ case SO_BROADCAST: @@ -1295,61 +1778,155 @@ int lwip_setsockopt (int s, int level, i case SO_REUSEPORT: #endif /* SO_REUSE */ /* UNIMPL case SO_USELOOPBACK: */ - if ( *(int*)optval ) { - sock->conn->pcb.tcp->so_options |= optname; + if (*(int*)optval) { + sock->conn->pcb.ip->so_options |= optname; } else { - sock->conn->pcb.tcp->so_options &= ~optname; + sock->conn->pcb.ip->so_options &= ~optname; } - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", s, optname, (*(int*)optval?"on":"off"))); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + sock->conn->recv_timeout = ( *(int*)optval ); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + sock->conn->recv_bufsize = ( *(int*)optval ); break; - } /* switch */ +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + if (*(int*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); + } + break; +#endif /* LWIP_UDP */ + } /* switch (optname) */ break; /* Level: IPPROTO_IP */ case IPPROTO_IP: - switch( optname ) { + switch (optname) { case IP_TTL: - sock->conn->pcb.tcp->ttl = (u8_t)(*(int*)optval); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %u\n", s, sock->conn->pcb.tcp->ttl)); + sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n", + s, sock->conn->pcb.ip->ttl)); break; case IP_TOS: - sock->conn->pcb.tcp->tos = (u8_t)(*(int*)optval); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %u\n", s, sock->conn->pcb.tcp->tos)); + sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", + s, sock->conn->pcb.ip->tos)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); + break; + case IP_MULTICAST_IF: + sock->conn->pcb.udp->multicast_ip.addr = ((struct in_addr*) optval)->s_addr; break; - } /* switch */ + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* If this is a TCP or a RAW socket, ignore these options. */ + struct ip_mreq *imr = (struct ip_mreq *)optval; + if(optname == IP_ADD_MEMBERSHIP){ + data->err = igmp_joingroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); + } else { + data->err = igmp_leavegroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); + } + if(data->err != ERR_OK) { + data->err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + } /* switch (optname) */ break; +#if LWIP_TCP /* Level: IPPROTO_TCP */ case IPPROTO_TCP: - switch( optname ) { + switch (optname) { case TCP_NODELAY: - if ( *(int*)optval ) { - sock->conn->pcb.tcp->flags |= TF_NODELAY; + if (*(int*)optval) { + tcp_nagle_disable(sock->conn->pcb.tcp); } else { - sock->conn->pcb.tcp->flags &= ~TF_NODELAY; + tcp_nagle_enable(sock->conn->pcb.tcp); } - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", s, (*(int *)optval)?"on":"off") ); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", + s, (*(int *)optval)?"on":"off") ); break; case TCP_KEEPALIVE: - sock->conn->pcb.tcp->keepalive = (u32_t)(*(int*)optval); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %lu\n", s, sock->conn->pcb.tcp->keepalive)); + sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); break; - } /* switch */ + case TCP_KEEPINTVL: + sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_intvl)); + break; + case TCP_KEEPCNT: + sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_cnt)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + + } /* switch (optname) */ break; - } /* switch */ - - sock_set_errno(sock, err); - return err ? -1 : 0; +#endif /* LWIP_TCP*/ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + if ((*(int*)optval != 0) && (*(int*)optval < 8)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = *(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + if ((*(int*)optval != 0) && (*(int*)optval < 8)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = *(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + } /* switch (level) */ + sys_sem_signal(sock->conn->op_completed); } -int lwip_ioctl(int s, long cmd, void *argp) +int +lwip_ioctl(int s, long cmd, void *argp) { struct lwip_socket *sock = get_socket(s); + u16_t buflen = 0; + s16_t recv_avail; - if(!sock) { - set_errno(EBADF); + if (!sock) return -1; - } switch (cmd) { case FIONREAD: @@ -1358,9 +1935,20 @@ int lwip_ioctl(int s, long cmd, void *ar return -1; } - *((u16_t*)argp) = sock->conn->recv_avail; + SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); + if (recv_avail < 0) + recv_avail = 0; + *((u16_t*)argp) = (u16_t)recv_avail; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %u\n", s, argp, *((u16_t*)argp))); + /* Check if there is data left from the last recv operation. /maq 041215 */ + if (sock->lastdata) { + buflen = netbuf_len(sock->lastdata); + buflen -= sock->lastoffset; + + *((u16_t*)argp) += buflen; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp))); sock_set_errno(sock, 0); return 0; @@ -1377,6 +1965,7 @@ int lwip_ioctl(int s, long cmd, void *ar LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); sock_set_errno(sock, ENOSYS); /* not yet implemented */ return -1; - } + } /* switch (cmd) */ } +#endif /* LWIP_SOCKET */ diff --git a/packages/net/lwip_tcpip/current/src/api/tcpip.c b/packages/net/lwip_tcpip/current/src/api/tcpip.c --- a/packages/net/lwip_tcpip/current/src/api/tcpip.c +++ b/packages/net/lwip_tcpip/current/src/api/tcpip.c @@ -1,8 +1,14 @@ +/** + * @file + * Sequential API Main thread module + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, + * 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, @@ -11,50 +17,66 @@ * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. + * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 * */ #include "lwip/opt.h" +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + #include "lwip/sys.h" - #include "lwip/memp.h" #include "lwip/pbuf.h" - -#include "lwip/ip.h" #include "lwip/ip_frag.h" -#include "lwip/udp.h" #include "lwip/tcp.h" +#include "lwip/autoip.h" +#include "lwip/dhcp.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/tcpip.h" +#include "lwip/init.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" -#include "lwip/tcpip.h" +/* global variables */ +static void (* tcpip_init_done)(void *arg); +static void *tcpip_init_done_arg; +static sys_mbox_t mbox = SYS_MBOX_NULL; -static void (* tcpip_init_done)(void *arg) = NULL; -static void *tcpip_init_done_arg; -static sys_mbox_t mbox; +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_sem_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ #if LWIP_TCP -static int tcpip_tcp_timer_active = 0; +/* global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ static void tcpip_tcp_timer(void *arg) { - (void)arg; + LWIP_UNUSED_ARG(arg); /* call TCP timer handler */ tcp_tmr(); @@ -69,6 +91,11 @@ tcpip_tcp_timer(void *arg) } #if !NO_SYS +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ void tcp_timer_needed(void) { @@ -83,116 +110,487 @@ tcp_timer_needed(void) #endif /* LWIP_TCP */ #if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ static void -ip_timer(void *data) +ip_reass_timer(void *arg) { + LWIP_UNUSED_ARG(arg); LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: ip_reass_tmr()\n")); ip_reass_tmr(); - sys_timeout(1000, ip_timer, NULL); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); } -#endif +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +/** + * The main lwIP thread. This thread has exclusive access to lwIP core functions + * (unless access to them is not locked). Other threads communicate with this + * thread using message boxes. + * + * It also starts all the timers to make sure they are running in the right + * thread context. + * + * @param arg unused argument + */ static void tcpip_thread(void *arg) { struct tcpip_msg *msg; - - (void)arg; + LWIP_UNUSED_ARG(arg); - ip_init(); -#if LWIP_UDP - udp_init(); -#endif -#if LWIP_TCP - tcp_init(); -#endif #if IP_REASSEMBLY - sys_timeout(1000, ip_timer, NULL); -#endif + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ + if (tcpip_init_done != NULL) { tcpip_init_done(tcpip_init_done_arg); } + LOCK_TCPIP_CORE(); while (1) { /* MAIN Loop */ sys_mbox_fetch(mbox, (void *)&msg); switch (msg->type) { +#if LWIP_NETCONN case TCPIP_MSG_API: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); - api_msg_input(msg->msg.apimsg); + msg->msg.apimsg->function(&(msg->msg.apimsg->msg)); break; - case TCPIP_MSG_INPUT: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: IP packet %p\n", (void *)msg)); - ip_input(msg->msg.inp.p, msg->msg.inp.netif); +#endif /* LWIP_NETCONN */ + + case TCPIP_MSG_INPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); +#if LWIP_ARP + if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) { + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_ARP */ + { ip_input(msg->msg.inp.p, msg->msg.inp.netif); + } + memp_free(MEMP_TCPIP_MSG_INPKT, msg); break; + +#if LWIP_NETIF_API + case TCPIP_MSG_NETIFAPI: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg)); + msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg)); + break; +#endif /* LWIP_NETIF_API */ + case TCPIP_MSG_CALLBACK: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); msg->msg.cb.f(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); break; + + case TCPIP_MSG_TIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + case TCPIP_MSG_UNTIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); + sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + default: break; } - memp_free(MEMP_TCPIP_MSG, msg); } } +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet, p->payload pointing to the Ethernet header or + * to an IP header (if netif doesn't got NETIF_FLAG_ETHARP flag) + * @param inp the network interface on which the packet was received + */ err_t tcpip_input(struct pbuf *p, struct netif *inp) { struct tcpip_msg *msg; - - msg = memp_malloc(MEMP_TCPIP_MSG); - if (msg == NULL) { - pbuf_free(p); - return ERR_MEM; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + if (sys_mbox_trypost(mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; + } + return ERR_OK; } - - msg->type = TCPIP_MSG_INPUT; - msg->msg.inp.p = p; - msg->msg.inp.netif = inp; - sys_mbox_post(mbox, msg); - return ERR_OK; + return ERR_VAL; } +/** + * Call a specific function in the thread context of + * tcpip_thread for easy access synchronization. + * A function called in that way may access lwIP core code + * without fearing concurrent access. + * + * @param f the function to call + * @param ctx parameter passed to f + * @param block 1 to block until the request is posted, 0 to non-blocking mode + * @return ERR_OK if the function was called, another err_t if not + */ err_t -tcpip_callback(void (*f)(void *ctx), void *ctx) +tcpip_callback_with_block(void (*f)(void *ctx), void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.f = f; + msg->msg.cb.ctx = ctx; + if (block) { + sys_mbox_post(mbox, msg); + } else { + if (sys_mbox_trypost(mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + } + return ERR_OK; + } + return ERR_VAL; +} + +/** + * call sys_timeout in tcpip_thread + * + * @param msec time in miliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} + +/** + * call sys_untimeout in tcpip_thread + * + * @param msec time in miliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_untimeout(sys_timeout_handler h, void *arg) { struct tcpip_msg *msg; - - msg = memp_malloc(MEMP_TCPIP_MSG); - if (msg == NULL) { - return ERR_MEM; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_UNTIMEOUT; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(mbox, msg); + return ERR_OK; } + return ERR_VAL; +} + +#if LWIP_NETCONN +/** + * Call the lower part of a netconn_* function + * This function is then running in the thread context + * of tcpip_thread and has exclusive access to lwIP core code. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_apimsg(struct api_msg *apimsg) +{ + struct tcpip_msg msg; - msg->type = TCPIP_MSG_CALLBACK; - msg->msg.cb.f = f; - msg->msg.cb.ctx = ctx; - sys_mbox_post(mbox, msg); - return ERR_OK; + if (mbox != SYS_MBOX_NULL) { + msg.type = TCPIP_MSG_API; + msg.msg.apimsg = apimsg; + sys_mbox_post(mbox, &msg); + sys_arch_sem_wait(apimsg->msg.conn->op_completed, 0); + return ERR_OK; + } + return ERR_VAL; } -void -tcpip_apimsg(struct api_msg *apimsg) +#if LWIP_TCPIP_CORE_LOCKING +/** + * Call the lower part of a netconn_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_apimsg()) + */ +err_t +tcpip_apimsg_lock(struct api_msg *apimsg) +{ + LOCK_TCPIP_CORE(); + apimsg->function(&(apimsg->msg)); + UNLOCK_TCPIP_CORE(); + return ERR_OK; + +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETCONN */ + +#if LWIP_NETIF_API +#if !LWIP_TCPIP_CORE_LOCKING +/** + * Much like tcpip_apimsg, but calls the lower part of a netifapi_* + * function. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return error code given back by the function that was called + */ +err_t +tcpip_netifapi(struct netifapi_msg* netifapimsg) { - struct tcpip_msg *msg; - msg = memp_malloc(MEMP_TCPIP_MSG); - if (msg == NULL) { - memp_free(MEMP_API_MSG, apimsg); - return; + struct tcpip_msg msg; + + if (mbox != SYS_MBOX_NULL) { + netifapimsg->msg.sem = sys_sem_new(0); + if (netifapimsg->msg.sem == SYS_SEM_NULL) { + netifapimsg->msg.err = ERR_MEM; + return netifapimsg->msg.err; + } + + msg.type = TCPIP_MSG_NETIFAPI; + msg.msg.netifapimsg = netifapimsg; + sys_mbox_post(mbox, &msg); + sys_sem_wait(netifapimsg->msg.sem); + sys_sem_free(netifapimsg->msg.sem); + return netifapimsg->msg.err; } - msg->type = TCPIP_MSG_API; - msg->msg.apimsg = apimsg; - sys_mbox_post(mbox, msg); + return ERR_VAL; } +#else /* !LWIP_TCPIP_CORE_LOCKING */ +/** + * Call the lower part of a netifapi_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_netifapi()) + */ +err_t +tcpip_netifapi_lock(struct netifapi_msg* netifapimsg) +{ + LOCK_TCPIP_CORE(); + netifapimsg->function(&(netifapimsg->msg)); + UNLOCK_TCPIP_CORE(); + return netifapimsg->msg.err; +} +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ +/** + * Initialize this module: + * - initialize all sub modules + * - start the tcpip_thread + * + * @param initfunc a function to call when tcpip_thread is running and finished initializing + * @param arg argument to pass to initfunc + */ void tcpip_init(void (* initfunc)(void *), void *arg) { + lwip_init(); + tcpip_init_done = initfunc; tcpip_init_done_arg = arg; - mbox = sys_mbox_new(); - sys_thread_new(tcpip_thread, NULL, TCPIP_THREAD_PRIO); + mbox = sys_mbox_new(TCPIP_MBOX_SIZE); +#if LWIP_TCPIP_CORE_LOCKING + lock_tcpip_core = sys_sem_new(1); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); +} + +/** + * Simple callback function used with tcpip_callback to free a pbuf + * (pbuf_free has a wrong signature for tcpip_callback) + * + * @param p The pbuf (chain) to be dereferenced. + */ +static void +pbuf_free_int(void *p) +{ + struct pbuf *q = p; + pbuf_free(q); } - +/** + * A simple wrapper function that allows you to free a pbuf from interrupt context. + * + * @param p The pbuf (chain) to be dereferenced. + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +pbuf_free_callback(struct pbuf *p) +{ + return tcpip_callback_with_block(pbuf_free_int, p, 0); +} +/** + * A simple wrapper function that allows you to free heap memory from + * interrupt context. + * + * @param m the heap memory to free + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +mem_free_callback(void *m) +{ + return tcpip_callback_with_block(mem_free, m, 0); +} +#endif /* !NO_SYS */ diff --git a/packages/net/lwip_tcpip/current/src/core/dhcp.c b/packages/net/lwip_tcpip/current/src/core/dhcp.c --- a/packages/net/lwip_tcpip/current/src/core/dhcp.c +++ b/packages/net/lwip_tcpip/current/src/core/dhcp.c @@ -1,7 +1,7 @@ /** * @file + * Dynamic Host Configuration Protocol client * - * Dynamic Host Configuration Protocol client */ /* @@ -67,67 +67,87 @@ * to remove the DHCP client. * */ - -#include - + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + #include "lwip/stats.h" #include "lwip/mem.h" #include "lwip/udp.h" #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/inet.h" +#include "lwip/sys.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" #include "netif/etharp.h" -#include "lwip/sys.h" -#include "lwip/opt.h" -#include "lwip/dhcp.h" +#include -#if LWIP_DHCP /* don't build if not configured for use in lwipopt.h */ +/** Default for DHCP_GLOBAL_XID is 0xABCD0000 + * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g. + * #define DHCP_GLOBAL_XID_HEADER "stdlib.h" + * #define DHCP_GLOBAL_XID rand() + */ +#ifdef DHCP_GLOBAL_XID_HEADER +#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */ +#endif -/** global transaction identifier, must be - * unique for each DHCP request. We simply increment, starting - * with this value (easy to match with a packet analyzer) */ -static u32_t xid = 0xABCD0000; +/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU + * MTU is checked to be big enough in dhcp_start */ +#define DHCP_MAX_MSG_LEN(netif) (netif->mtu) +#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576 +/** Minimum length for reply before packet is parsed */ +#define DHCP_MIN_REPLY_LEN 44 -/** DHCP client state machine functions */ +#define REBOOT_TRIES 2 + +/* DHCP client state machine functions */ static void dhcp_handle_ack(struct netif *netif); static void dhcp_handle_nak(struct netif *netif); static void dhcp_handle_offer(struct netif *netif); static err_t dhcp_discover(struct netif *netif); static err_t dhcp_select(struct netif *netif); +static void dhcp_bind(struct netif *netif); +#if DHCP_DOES_ARP_CHECK static void dhcp_check(struct netif *netif); -static void dhcp_bind(struct netif *netif); static err_t dhcp_decline(struct netif *netif); +#endif /* DHCP_DOES_ARP_CHECK */ static err_t dhcp_rebind(struct netif *netif); +static err_t dhcp_reboot(struct netif *netif); static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); -/** receive, unfold, parse and free incoming messages */ +/* receive, unfold, parse and free incoming messages */ static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); -static err_t dhcp_unfold_reply(struct dhcp *dhcp); +static err_t dhcp_unfold_reply(struct dhcp *dhcp, struct pbuf *p); static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type); static u8_t dhcp_get_option_byte(u8_t *ptr); +#if 0 static u16_t dhcp_get_option_short(u8_t *ptr); +#endif static u32_t dhcp_get_option_long(u8_t *ptr); static void dhcp_free_reply(struct dhcp *dhcp); -/** set the DHCP timers */ +/* set the DHCP timers */ static void dhcp_timeout(struct netif *netif); static void dhcp_t1_timeout(struct netif *netif); static void dhcp_t2_timeout(struct netif *netif); -/** build outgoing messages */ -/** create a DHCP request, fill in common headers */ +/* build outgoing messages */ +/* create a DHCP request, fill in common headers */ static err_t dhcp_create_request(struct netif *netif); -/** free a DHCP request */ +/* free a DHCP request */ static void dhcp_delete_request(struct netif *netif); -/** add a DHCP option (type, then length in bytes) */ +/* add a DHCP option (type, then length in bytes) */ static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); -/** add option values */ +/* add option values */ static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); static void dhcp_option_short(struct dhcp *dhcp, u16_t value); static void dhcp_option_long(struct dhcp *dhcp, u32_t value); -/** always add the DHCP options trailer to end and pad */ +/* always add the DHCP options trailer to end and pad */ static void dhcp_option_trailer(struct dhcp *dhcp); /** @@ -137,66 +157,80 @@ static void dhcp_option_trailer(struct d * NAK means the client asked for something non-sensible, for * example when it tries to renew a lease obtained on another network. * - * We back-off and will end up restarting a fresh DHCP negotiation later. + * We clear any existing set IP address and restart DHCP negotiation + * afresh (as per RFC2131 3.2.3). * - * @param state pointer to DHCP state structure + * @param netif the netif under DHCP control */ -static void dhcp_handle_nak(struct netif *netif) { +static void +dhcp_handle_nak(struct netif *netif) +{ struct dhcp *dhcp = netif->dhcp; - u16_t msecs = 10 * 1000; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); - dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_handle_nak(): set request timeout %"U16_F" msecs\n", msecs)); + /* Set the interface down since the address must no longer be used, as per RFC2131 */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + /* Change to a defined state */ dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* We can immediately restart discovery */ + dhcp_discover(netif); } +#if DHCP_DOES_ARP_CHECK /** * Checks if the offered IP address is already in use. * * It does so by sending an ARP request for the offered address and * entering CHECKING state. If no ARP reply is received within a small * interval, the address is assumed to be free for use by us. + * + * @param netif the netif under DHCP control */ -static void dhcp_check(struct netif *netif) +static void +dhcp_check(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; err_t result; u16_t msecs; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], (s16_t)netif->name[1])); + dhcp_set_state(dhcp, DHCP_CHECKING); /* create an ARP query for the offered IP address, expecting that no host responds, as the IP address should not be in use. */ result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); if (result != ERR_OK) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_check: could not perform ARP query\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n")); } dhcp->tries++; msecs = 500; dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); - dhcp_set_state(dhcp, DHCP_CHECKING); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); } +#endif /* DHCP_DOES_ARP_CHECK */ /** * Remember the configuration offered by a DHCP server. * - * @param state pointer to DHCP state structure + * @param netif the netif under DHCP control */ -static void dhcp_handle_offer(struct netif *netif) +static void +dhcp_handle_offer(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; /* obtain the server address */ u8_t *option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SERVER_ID); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); - if (option_ptr != NULL) - { + if (option_ptr != NULL) { dhcp->server_ip_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); - LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", dhcp->server_ip_addr.addr)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", dhcp->server_ip_addr.addr)); /* remember offered address */ ip_addr_set(&dhcp->offered_ip_addr, (struct ip_addr *)&dhcp->msg_in->yiaddr); - LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); dhcp_select(netif); } @@ -210,22 +244,27 @@ static void dhcp_handle_offer(struct net * @param netif the netif under DHCP control * @return lwIP specific error (see error.h) */ -static err_t dhcp_select(struct netif *netif) +static err_t +dhcp_select(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; err_t result; - u32_t msecs; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + u16_t msecs; +#if LWIP_NETIF_HOSTNAME + const char *p; +#endif /* LWIP_NETIF_HOSTNAME */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + dhcp_set_state(dhcp, DHCP_REQUESTING); /* create and initialize the DHCP message header */ result = dhcp_create_request(netif); - if (result == ERR_OK) - { + if (result == ERR_OK) { dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); dhcp_option_byte(dhcp, DHCP_REQUEST); dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); - dhcp_option_short(dhcp, 576); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); /* MUST request the offered IP address */ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); @@ -240,28 +279,31 @@ static err_t dhcp_select(struct netif *n dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); +#if LWIP_NETIF_HOSTNAME + p = (const char*)netif->hostname; + if (p != NULL) { + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, strlen(p)); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + dhcp_option_trailer(dhcp); /* shrink the pbuf to the actual content length */ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); - /* TODO: we really should bind to a specific local interface here - but we cannot specify an unconfigured netif as it is addressless */ - udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); /* send broadcast to any DHCP server */ - udp_connect(dhcp->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); - udp_send(dhcp->pcb, dhcp->p_out); - /* reconnect to any (or to server here?!) */ - udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); dhcp_delete_request(netif); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_select: REQUESTING\n")); - dhcp_set_state(dhcp, DHCP_REQUESTING); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n")); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_select: could not allocate DHCP request\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n")); } dhcp->tries++; - msecs = dhcp->tries < 4 ? dhcp->tries * 1000 : 4 * 1000; + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_select(): set request timeout %"U32_F" msecs\n", msecs)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs)); return result; } @@ -269,22 +311,23 @@ static err_t dhcp_select(struct netif *n * The DHCP timer that checks for lease renewal/rebind timeouts. * */ -void dhcp_coarse_tmr() +void +dhcp_coarse_tmr() { struct netif *netif = netif_list; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_coarse_tmr()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n")); /* iterate through all network interfaces */ while (netif != NULL) { /* only act on DHCP configured interfaces */ if (netif->dhcp != NULL) { /* timer is active (non zero), and triggers (zeroes) now? */ if (netif->dhcp->t2_timeout-- == 1) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); /* this clients' rebind timeout triggered */ dhcp_t2_timeout(netif); /* timer is active (non zero), and triggers (zeroes) now */ } else if (netif->dhcp->t1_timeout-- == 1) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); /* this clients' renewal timeout triggered */ dhcp_t1_timeout(netif); } @@ -301,17 +344,22 @@ void dhcp_coarse_tmr() * This timer checks whether an outstanding DHCP request is timed out. * */ -void dhcp_fine_tmr() +void +dhcp_fine_tmr() { struct netif *netif = netif_list; /* loop through netif's */ while (netif != NULL) { /* only act on DHCP configured interfaces */ if (netif->dhcp != NULL) { - /* timer is active (non zero), and is about to trigger now */ - if (netif->dhcp->request_timeout-- == 1) { + /* timer is active (non zero), and is about to trigger now */ + if (netif->dhcp->request_timeout > 1) { + netif->dhcp->request_timeout--; + } + else if (netif->dhcp->request_timeout == 1) { + netif->dhcp->request_timeout--; /* { netif->dhcp->request_timeout == 0 } */ - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); /* this clients' request timeout triggered */ dhcp_timeout(netif); } @@ -328,29 +376,30 @@ void dhcp_fine_tmr() * timed out, indicating no response was received in time. * * @param netif the netif under DHCP control - * */ -static void dhcp_timeout(struct netif *netif) +static void +dhcp_timeout(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_timeout()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n")); /* back-off period has passed, or server selection timed out */ if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); dhcp_discover(netif); /* receiving the requested lease timed out */ } else if (dhcp->state == DHCP_REQUESTING) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); if (dhcp->tries <= 5) { dhcp_select(netif); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); dhcp_release(netif); dhcp_discover(netif); } +#if DHCP_DOES_ARP_CHECK /* received no ARP reply for the offered address (which is good) */ } else if (dhcp->state == DHCP_CHECKING) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); if (dhcp->tries <= 1) { dhcp_check(netif); /* no ARP replies on the offered address, @@ -359,23 +408,30 @@ static void dhcp_timeout(struct netif *n /* bind the interface to the offered address */ dhcp_bind(netif); } +#endif /* DHCP_DOES_ARP_CHECK */ } /* did not get response to renew request? */ else if (dhcp->state == DHCP_RENEWING) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); /* just retry renewal */ /* note that the rebind timer will eventually time-out if renew does not work */ dhcp_renew(netif); /* did not get response to rebind request? */ } else if (dhcp->state == DHCP_REBINDING) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); if (dhcp->tries <= 8) { dhcp_rebind(netif); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); dhcp_release(netif); dhcp_discover(netif); } + } else if (dhcp->state == DHCP_REBOOTING) { + if (dhcp->tries < REBOOT_TRIES) { + dhcp_reboot(netif); + } else { + dhcp_discover(netif); + } } } @@ -384,14 +440,15 @@ static void dhcp_timeout(struct netif *n * * @param netif the netif under DHCP control */ -static void dhcp_t1_timeout(struct netif *netif) +static void +dhcp_t1_timeout(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; - LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_t1_timeout()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n")); if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) { /* just retry to renew - note that the rebind timer (t2) will * eventually time-out if renew tries fail. */ - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t1_timeout(): must renew\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t1_timeout(): must renew\n")); dhcp_renew(netif); } } @@ -399,23 +456,27 @@ static void dhcp_t1_timeout(struct netif /** * The rebind period has timed out. * + * @param netif the netif under DHCP control */ -static void dhcp_t2_timeout(struct netif *netif) +static void +dhcp_t2_timeout(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t2_timeout()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n")); if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) { /* just retry to rebind */ - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t2_timeout(): must rebind\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout(): must rebind\n")); dhcp_rebind(netif); } } /** + * Handle a DHCP ACK packet * * @param netif the netif under DHCP control */ -static void dhcp_handle_ack(struct netif *netif) +static void +dhcp_handle_ack(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; u8_t *option_ptr; @@ -490,13 +551,19 @@ static void dhcp_handle_ack(struct netif option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_DNS_SERVER); if (option_ptr != NULL) { u8_t n; - dhcp->dns_count = dhcp_get_option_byte(&option_ptr[1]); + dhcp->dns_count = dhcp_get_option_byte(&option_ptr[1]) / (u32_t)sizeof(struct ip_addr); /* limit to at most DHCP_MAX_DNS DNS servers */ - if (dhcp->dns_count > DHCP_MAX_DNS) dhcp->dns_count = DHCP_MAX_DNS; - for (n = 0; n < dhcp->dns_count; n++) - { - dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2+(n<<2)])); + if (dhcp->dns_count > DHCP_MAX_DNS) + dhcp->dns_count = DHCP_MAX_DNS; + for (n = 0; n < dhcp->dns_count; n++) { + dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2 + n * 4])); +#if LWIP_DNS + dns_setserver( n, (struct ip_addr *)(&(dhcp->offered_dns_addr[n].addr))); +#endif /* LWIP_DNS */ } +#if LWIP_DNS + dns_setserver( n, (struct ip_addr *)(&ip_addr_any)); +#endif /* LWIP_DNS */ } } @@ -511,44 +578,67 @@ static void dhcp_handle_ack(struct netif * @return lwIP error code * - ERR_OK - No error * - ERR_MEM - Out of memory - * */ -err_t dhcp_start(struct netif *netif) +err_t +dhcp_start(struct netif *netif) { - struct dhcp *dhcp = netif->dhcp; + struct dhcp *dhcp; err_t result = ERR_OK; - LWIP_ASSERT("netif != NULL", netif != NULL); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Remove the flag that says this netif is handled by DHCP, + it is set when we succeeded starting. */ netif->flags &= ~NETIF_FLAG_DHCP; + /* check MTU of the netif */ + if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n")); + return ERR_MEM; + } + /* no DHCP client attached yet? */ if (dhcp == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); dhcp = mem_malloc(sizeof(struct dhcp)); if (dhcp == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); return ERR_MEM; } /* store this dhcp client in the netif */ netif->dhcp = dhcp; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): allocated dhcp")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp")); /* already has DHCP client attached */ } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE | 3, ("dhcp_start(): restarting DHCP configuration\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n")); + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + } + LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL && + dhcp->options_in == NULL && dhcp->options_in_len == 0); } - - /* clear data structure */ - memset(dhcp, 0, sizeof(struct dhcp)); + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); /* allocate UDP PCB */ - dhcp->pcb = udp_new(); - if (dhcp->pcb == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); - mem_free((void *)dhcp); - netif->dhcp = dhcp = NULL; - return ERR_MEM; - } - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); + mem_free((void *)dhcp); + netif->dhcp = dhcp = NULL; + return ERR_MEM; + } +#if IP_SOF_BROADCAST + dhcp->pcb->so_options|=SOF_BROADCAST; +#endif /* IP_SOF_BROADCAST */ + /* set up local and remote port for the pcb */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* set up the recv callback and argument */ + udp_recv(dhcp->pcb, dhcp_recv, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); /* (re)start the DHCP negotiation */ result = dhcp_discover(netif); if (result != ERR_OK) { @@ -556,6 +646,7 @@ err_t dhcp_start(struct netif *netif) dhcp_stop(netif); return ERR_MEM; } + /* Set the flag that says this netif is handled by DHCP. */ netif->flags |= NETIF_FLAG_DHCP; return result; } @@ -568,28 +659,28 @@ err_t dhcp_start(struct netif *netif) * configuration, it is just here to be nice to the network. * * @param netif The lwIP network interface - * */ -void dhcp_inform(struct netif *netif) +void +dhcp_inform(struct netif *netif) { - struct dhcp *dhcp; + struct dhcp *dhcp, *old_dhcp; err_t result = ERR_OK; dhcp = mem_malloc(sizeof(struct dhcp)); if (dhcp == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform(): could not allocate dhcp\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not allocate dhcp\n")); return; } - netif->dhcp = dhcp; memset(dhcp, 0, sizeof(struct dhcp)); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_inform(): allocated dhcp\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): allocated dhcp\n")); dhcp->pcb = udp_new(); if (dhcp->pcb == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform(): could not obtain pcb")); - mem_free((void *)dhcp); - return; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb")); + goto free_dhcp_and_return; } - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + old_dhcp = netif->dhcp; + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); /* create and initialize the DHCP message header */ result = dhcp_create_request(netif); if (result == ERR_OK) { @@ -598,29 +689,57 @@ void dhcp_inform(struct netif *netif) dhcp_option_byte(dhcp, DHCP_INFORM); dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); - /* TODO: use netif->mtu ?! */ - dhcp_option_short(dhcp, 576); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); dhcp_option_trailer(dhcp); pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); +#if IP_SOF_BROADCAST + dhcp->pcb->so_options|=SOF_BROADCAST; +#endif /* IP_SOF_BROADCAST */ udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); - udp_connect(dhcp->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_inform: INFORMING\n")); - udp_send(dhcp->pcb, dhcp->p_out); - udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); dhcp_delete_request(netif); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform: could not allocate DHCP request\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n")); } - if (dhcp != NULL) - { - if (dhcp->pcb != NULL) udp_remove(dhcp->pcb); - dhcp->pcb = NULL; - mem_free((void *)dhcp); - netif->dhcp = NULL; + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + netif->dhcp = old_dhcp; +free_dhcp_and_return: + mem_free((void *)dhcp); +} + +/** Handle a possible change in the network configuration. + * + * This enters the REBOOTING state to verify that the currently bound + * address is still valid. + */ +void +dhcp_network_changed(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + if (!dhcp) + return; + switch (dhcp->state) { + case DHCP_REBINDING: + case DHCP_RENEWING: + case DHCP_BOUND: + case DHCP_REBOOTING: + netif_set_down(netif); + dhcp->tries = 0; + dhcp_reboot(netif); + break; + case DHCP_OFF: + /* stay off */ + break; + default: + dhcp->tries = 0; + dhcp_discover(netif); + break; } } @@ -628,21 +747,22 @@ void dhcp_inform(struct netif *netif) /** * Match an ARP reply with the offered IP address. * + * @param netif the network interface on which the reply was received * @param addr The IP address we received a reply from - * */ void dhcp_arp_reply(struct netif *netif, struct ip_addr *addr) { - LWIP_ASSERT("netif != NULL", netif != NULL); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_arp_reply()\n")); + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n")); /* is a DHCP client doing an ARP check? */ if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", addr->addr)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", addr->addr)); /* did a host respond with the address we were offered by the DHCP server? */ if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) { /* we will not accept the offered address */ - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE | 1, ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); dhcp_decline(netif); } } @@ -654,24 +774,23 @@ void dhcp_arp_reply(struct netif *netif, * Tell the DHCP server we do not accept the offered address. * One reason to decline the lease is when we find out the address * is already in use by another host (through ARP). + * + * @param netif the netif under DHCP control */ -static err_t dhcp_decline(struct netif *netif) +static err_t +dhcp_decline(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; err_t result = ERR_OK; u16_t msecs; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_decline()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n")); dhcp_set_state(dhcp, DHCP_BACKING_OFF); /* create and initialize the DHCP message header */ result = dhcp_create_request(netif); - if (result == ERR_OK) - { + if (result == ERR_OK) { dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); dhcp_option_byte(dhcp, DHCP_DECLINE); - dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); - dhcp_option_short(dhcp, 576); - dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); @@ -679,20 +798,18 @@ static err_t dhcp_decline(struct netif * /* resize pbuf to reflect true size of options */ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); - udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); - /* @todo: should we really connect here? we are performing sendto() */ - udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); /* per section 4.4.4, broadcast DECLINE messages */ - udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); dhcp_delete_request(netif); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n")); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_decline: could not allocate DHCP request\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_decline: could not allocate DHCP request\n")); } dhcp->tries++; msecs = 10*1000; dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); return result; } #endif @@ -701,24 +818,26 @@ static err_t dhcp_decline(struct netif * /** * Start the DHCP process, discover a DHCP server. * + * @param netif the netif under DHCP control */ -static err_t dhcp_discover(struct netif *netif) +static err_t +dhcp_discover(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; err_t result = ERR_OK; u16_t msecs; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_discover()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n")); ip_addr_set(&dhcp->offered_ip_addr, IP_ADDR_ANY); + dhcp_set_state(dhcp, DHCP_SELECTING); /* create and initialize the DHCP message header */ result = dhcp_create_request(netif); - if (result == ERR_OK) - { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: making request\n")); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); dhcp_option_byte(dhcp, DHCP_DISCOVER); dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); - dhcp_option_short(dhcp, 576); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); @@ -728,26 +847,27 @@ static err_t dhcp_discover(struct netif dhcp_option_trailer(dhcp); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n")); pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); - /* set receive callback function with netif as user data */ - udp_recv(dhcp->pcb, dhcp_recv, netif); - udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); - udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); - udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n")); dhcp_delete_request(netif); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_discover: SELECTING\n")); - dhcp_set_state(dhcp, DHCP_SELECTING); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n")); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_discover: could not allocate DHCP request\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n")); } dhcp->tries++; - msecs = dhcp->tries < 4 ? (dhcp->tries + 1) * 1000 : 10 * 1000; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) { + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON; + autoip_start(netif); + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); return result; } @@ -757,28 +877,43 @@ static err_t dhcp_discover(struct netif * * @param netif network interface to bind to the offered address */ -static void dhcp_bind(struct netif *netif) +static void +dhcp_bind(struct netif *netif) { - struct dhcp *dhcp = netif->dhcp; + u32_t timeout; + struct dhcp *dhcp; struct ip_addr sn_mask, gw_addr; - LWIP_ASSERT("dhcp_bind: netif != NULL", netif != NULL); - LWIP_ASSERT("dhcp_bind: dhcp != NULL", dhcp != NULL); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); /* temporary DHCP lease? */ if (dhcp->offered_t1_renew != 0xffffffffUL) { /* set renewal period timer */ - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); - dhcp->t1_timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; - if (dhcp->t1_timeout == 0) dhcp->t1_timeout = 1; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t1_timeout = (u16_t)timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); } /* set renewal period timer */ if (dhcp->offered_t2_rebind != 0xffffffffUL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); - dhcp->t2_timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; - if (dhcp->t2_timeout == 0) dhcp->t2_timeout = 1; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t2_timeout = (u16_t)timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); } /* copy offered network mask */ ip_addr_set(&sn_mask, &dhcp->offered_sn_mask); @@ -788,9 +923,13 @@ static void dhcp_bind(struct netif *neti if (sn_mask.addr == 0) { /* choose a safe subnet mask given the network class */ u8_t first_octet = ip4_addr1(&sn_mask); - if (first_octet <= 127) sn_mask.addr = htonl(0xff000000); - else if (first_octet >= 192) sn_mask.addr = htonl(0xffffff00); - else sn_mask.addr = htonl(0xffff0000); + if (first_octet <= 127) { + sn_mask.addr = htonl(0xff000000); + } else if (first_octet >= 192) { + sn_mask.addr = htonl(0xffffff00); + } else { + sn_mask.addr = htonl(0xffff0000); + } } ip_addr_set(&gw_addr, &dhcp->offered_gw_addr); @@ -802,11 +941,18 @@ static void dhcp_bind(struct netif *neti gw_addr.addr |= htonl(0x00000001); } - LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); netif_set_ipaddr(netif, &dhcp->offered_ip_addr); - LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", sn_mask.addr)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", sn_mask.addr)); netif_set_netmask(netif, &sn_mask); - LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", gw_addr.addr)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", gw_addr.addr)); netif_set_gw(netif, &gw_addr); /* bring the interface up */ netif_set_up(netif); @@ -819,12 +965,16 @@ static void dhcp_bind(struct netif *neti * * @param netif network interface which must renew its lease */ -err_t dhcp_renew(struct netif *netif) +err_t +dhcp_renew(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; err_t result; u16_t msecs; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_renew()\n")); +#if LWIP_NETIF_HOSTNAME + const char *p; +#endif /* LWIP_NETIF_HOSTNAME */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n")); dhcp_set_state(dhcp, DHCP_RENEWING); /* create and initialize the DHCP message header */ @@ -835,8 +985,17 @@ err_t dhcp_renew(struct netif *netif) dhcp_option_byte(dhcp, DHCP_REQUEST); dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); - /* TODO: use netif->mtu in some way */ - dhcp_option_short(dhcp, 576); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + p = (const char*)netif->hostname; + if (p != NULL) { + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, strlen(p)); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } +#endif /* LWIP_NETIF_HOSTNAME */ #if 0 dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); @@ -852,20 +1011,18 @@ err_t dhcp_renew(struct netif *netif) pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); - udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); - udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT); - udp_send(dhcp->pcb, dhcp->p_out); + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); dhcp_delete_request(netif); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_renew: RENEWING\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n")); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_renew: could not allocate DHCP request\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n")); } dhcp->tries++; /* back-off on retries, but to a maximum of 20 seconds */ msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); return result; } @@ -874,24 +1031,37 @@ err_t dhcp_renew(struct netif *netif) * * @param netif network interface which must rebind with a DHCP server */ -static err_t dhcp_rebind(struct netif *netif) +static err_t +dhcp_rebind(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; err_t result; u16_t msecs; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind()\n")); +#if LWIP_NETIF_HOSTNAME + const char *p; +#endif /* LWIP_NETIF_HOSTNAME */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n")); dhcp_set_state(dhcp, DHCP_REBINDING); /* create and initialize the DHCP message header */ result = dhcp_create_request(netif); - if (result == ERR_OK) - { + if (result == ERR_OK) { dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); dhcp_option_byte(dhcp, DHCP_REQUEST); dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); - dhcp_option_short(dhcp, 576); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + p = (const char*)netif->hostname; + if (p != NULL) { + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, strlen(p)); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } +#endif /* LWIP_NETIF_HOSTNAME */ #if 0 dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); @@ -905,34 +1075,78 @@ static err_t dhcp_rebind(struct netif *n pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); - /* set remote IP association to any DHCP server */ - udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); - udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); /* broadcast to server */ - udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); dhcp_delete_request(netif); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind: REBINDING\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n")); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_rebind: could not allocate DHCP request\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n")); } dhcp->tries++; msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); return result; } /** + * Enter REBOOTING state to verify an existing lease + * + * @param netif network interface which must reboot + */ +static err_t +dhcp_reboot(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n")); + dhcp_set_state(dhcp, DHCP_REBOOTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** * Release a DHCP lease. * * @param netif network interface which must release its lease */ -err_t dhcp_release(struct netif *netif) +err_t +dhcp_release(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; err_t result; u16_t msecs; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_release()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); /* idle DHCP client */ dhcp_set_state(dhcp, DHCP_OFF); @@ -953,18 +1167,16 @@ err_t dhcp_release(struct netif *netif) pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); - udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); - udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT); - udp_send(dhcp->pcb, dhcp->p_out); + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); dhcp_delete_request(netif); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_release: could not allocate DHCP request\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n")); } dhcp->tries++; msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); /* bring the interface down */ netif_set_down(netif); /* remove IP address from interface */ @@ -975,32 +1187,36 @@ err_t dhcp_release(struct netif *netif) /* TODO: netif_down(netif); */ return result; } + /** * Remove the DHCP client from the interface. * * @param netif The network interface to stop DHCP on */ -void dhcp_stop(struct netif *netif) +void +dhcp_stop(struct netif *netif) { struct dhcp *dhcp = netif->dhcp; - LWIP_ASSERT("dhcp_stop: netif != NULL", netif != NULL); + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + /* Remove the flag that says this netif is handled by DHCP. */ + netif->flags &= ~NETIF_FLAG_DHCP; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_stop()\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n")); /* netif is DHCP configured? */ - if (dhcp != NULL) - { - if (dhcp->pcb != NULL) - { + if (dhcp != NULL) { +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + if (dhcp->pcb != NULL) { udp_remove(dhcp->pcb); dhcp->pcb = NULL; } - if (dhcp->p != NULL) - { - pbuf_free(dhcp->p); - dhcp->p = NULL; - } - /* free unfolded reply */ - dhcp_free_reply(dhcp); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL && + dhcp->options_in == NULL && dhcp->options_in_len == 0); mem_free((void *)dhcp); netif->dhcp = NULL; } @@ -1013,10 +1229,10 @@ void dhcp_stop(struct netif *netif) * * TODO: we might also want to reset the timeout here? */ -static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +static void +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) { - if (new_state != dhcp->state) - { + if (new_state != dhcp->state) { dhcp->state = new_state; dhcp->tries = 0; } @@ -1027,9 +1243,10 @@ static void dhcp_set_state(struct dhcp * * DHCP message. * */ -static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +static void +dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) { - LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN); + LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN); dhcp->msg_out->options[dhcp->options_out_len++] = option_type; dhcp->msg_out->options[dhcp->options_out_len++] = option_len; } @@ -1037,24 +1254,29 @@ static void dhcp_option(struct dhcp *dhc * Concatenate a single byte to the outgoing DHCP message. * */ -static void dhcp_option_byte(struct dhcp *dhcp, u8_t value) +static void +dhcp_option_byte(struct dhcp *dhcp, u8_t value) { - LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); dhcp->msg_out->options[dhcp->options_out_len++] = value; } -static void dhcp_option_short(struct dhcp *dhcp, u16_t value) + +static void +dhcp_option_short(struct dhcp *dhcp, u16_t value) { - LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN); - dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0xff00U) >> 8; - dhcp->msg_out->options[dhcp->options_out_len++] = value & 0x00ffU; + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU); } -static void dhcp_option_long(struct dhcp *dhcp, u32_t value) + +static void +dhcp_option_long(struct dhcp *dhcp, u32_t value) { - LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN); - dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0xff000000UL) >> 24; - dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x00ff0000UL) >> 16; - dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x0000ff00UL) >> 8; - dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x000000ffUL); + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); } /** @@ -1067,70 +1289,56 @@ static void dhcp_option_long(struct dhcp * use that further on. * */ -static err_t dhcp_unfold_reply(struct dhcp *dhcp) +static err_t +dhcp_unfold_reply(struct dhcp *dhcp, struct pbuf *p) { - struct pbuf *p = dhcp->p; - u8_t *ptr; - u16_t i; - u16_t j = 0; - LWIP_ASSERT("dhcp->p != NULL", dhcp->p != NULL); + u16_t ret; + LWIP_ERROR("dhcp != NULL", (dhcp != NULL), return ERR_ARG;); /* free any left-overs from previous unfolds */ dhcp_free_reply(dhcp); /* options present? */ - if (dhcp->p->tot_len > (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)) - { - dhcp->options_in_len = dhcp->p->tot_len - (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + if (p->tot_len > (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)) { + dhcp->options_in_len = p->tot_len - (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); dhcp->options_in = mem_malloc(dhcp->options_in_len); - if (dhcp->options_in == NULL) - { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->options\n")); + if (dhcp->options_in == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_unfold_reply(): could not allocate dhcp->options\n")); + dhcp->options_in_len = 0; return ERR_MEM; } } dhcp->msg_in = mem_malloc(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); - if (dhcp->msg_in == NULL) - { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->msg_in\n")); - mem_free((void *)dhcp->options_in); - dhcp->options_in = NULL; + if (dhcp->msg_in == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_unfold_reply(): could not allocate dhcp->msg_in\n")); + if (dhcp->options_in != NULL) { + mem_free(dhcp->options_in); + dhcp->options_in = NULL; + dhcp->options_in_len = 0; + } return ERR_MEM; } - ptr = (u8_t *)dhcp->msg_in; - /* proceed through struct dhcp_msg */ - for (i = 0; i < sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN; i++) - { - *ptr++ = ((u8_t *)p->payload)[j++]; - /* reached end of pbuf? */ - if (j == p->len) - { - /* proceed to next pbuf in chain */ - p = p->next; - j = 0; - } + /** copy the DHCP message without options */ + ret = pbuf_copy_partial(p, dhcp->msg_in, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN, 0); + LWIP_ASSERT("ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN", ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes into dhcp->msg_in[]\n", + sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)); + + if (dhcp->options_in != NULL) { + /** copy the DHCP options */ + ret = pbuf_copy_partial(p, dhcp->options_in, dhcp->options_in_len, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + LWIP_ASSERT("ret == dhcp->options_in_len", ret == dhcp->options_in_len); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes to dhcp->options_in[]\n", + dhcp->options_in_len)); } - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes into dhcp->msg_in[]\n", i)); - if (dhcp->options_in != NULL) { - ptr = (u8_t *)dhcp->options_in; - /* proceed through options */ - for (i = 0; i < dhcp->options_in_len; i++) { - *ptr++ = ((u8_t *)p->payload)[j++]; - /* reached end of pbuf? */ - if (j == p->len) { - /* proceed to next pbuf in chain */ - p = p->next; - j = 0; - } - } - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes to dhcp->options_in[]\n", i)); - } + LWIP_UNUSED_ARG(ret); return ERR_OK; } /** * Free the incoming DHCP message including contiguous copy of * its DHCP options. - * */ static void dhcp_free_reply(struct dhcp *dhcp) { @@ -1139,14 +1347,13 @@ static void dhcp_free_reply(struct dhcp dhcp->msg_in = NULL; } if (dhcp->options_in) { - mem_free((void *)dhcp->options_in); + mem_free(dhcp->options_in); dhcp->options_in = NULL; dhcp->options_in_len = 0; } LWIP_DEBUGF(DHCP_DEBUG, ("dhcp_free_reply(): free'd\n")); } - /** * If an incoming DHCP message is in response to us, then trigger the state machine */ @@ -1158,61 +1365,63 @@ static void dhcp_recv(void *arg, struct u8_t *options_ptr; u8_t msg_type; u8_t i; - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, (u16_t)(ntohl(addr->addr) >> 24 & 0xff), (u16_t)(ntohl(addr->addr) >> 16 & 0xff), (u16_t)(ntohl(addr->addr) >> 8 & 0xff), (u16_t)(ntohl(addr->addr) & 0xff), port)); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); /* prevent warnings about unused arguments */ - (void)pcb; (void)addr; (void)port; - dhcp->p = p; - /* TODO: check packet length before reading them */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL && + dhcp->options_in == NULL && dhcp->options_in_len == 0); + + if (p->len < DHCP_MIN_REPLY_LEN) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message too short\n")); + goto free_pbuf_and_return; + } + if (reply_msg->op != DHCP_BOOTREPLY) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); - pbuf_free(p); - dhcp->p = NULL; - return; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + goto free_pbuf_and_return; } /* iterate through hardware address and match against DHCP message */ for (i = 0; i < netif->hwaddr_len; i++) { if (netif->hwaddr[i] != reply_msg->chaddr[i]) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); - pbuf_free(p); - dhcp->p = NULL; - return; + goto free_pbuf_and_return; } } /* match transaction ID against what we expected */ if (ntohl(reply_msg->xid) != dhcp->xid) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("transaction id mismatch\n")); - pbuf_free(p); - dhcp->p = NULL; - return; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid)); + goto free_pbuf_and_return; } /* option fields could be unfold? */ - if (dhcp_unfold_reply(dhcp) != ERR_OK) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("problem unfolding DHCP message - too short on memory?\n")); - pbuf_free(p); - dhcp->p = NULL; - return; + if (dhcp_unfold_reply(dhcp, p) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("problem unfolding DHCP message - too short on memory?\n")); + goto free_pbuf_and_return; } - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); /* obtain pointer to DHCP message type */ options_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_MESSAGE_TYPE); if (options_ptr == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); - pbuf_free(p); - dhcp->p = NULL; - return; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + goto free_pbuf_and_return; } /* read DHCP message type */ msg_type = dhcp_get_option_byte(options_ptr + 2); /* message type is DHCP ACK? */ if (msg_type == DHCP_ACK) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_ACK received\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n")); /* in requesting state? */ if (dhcp->state == DHCP_REQUESTING) { dhcp_handle_ack(netif); @@ -1235,35 +1444,66 @@ static void dhcp_recv(void *arg, struct else if ((msg_type == DHCP_NAK) && ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_NAK received\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n")); dhcp->request_timeout = 0; dhcp_handle_nak(netif); } /* received a DHCP_OFFER in DHCP_SELECTING state? */ else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_OFFER received in DHCP_SELECTING state\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n")); dhcp->request_timeout = 0; /* remember offered lease */ dhcp_handle_offer(netif); } +free_pbuf_and_return: + dhcp_free_reply(dhcp); pbuf_free(p); - dhcp->p = NULL; } - -static err_t dhcp_create_request(struct netif *netif) +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_create_request(struct netif *netif) { - struct dhcp *dhcp = netif->dhcp; + struct dhcp *dhcp; u16_t i; +#ifndef DHCP_GLOBAL_XID + /** default global transaction identifier starting value (easy to match + * with a packet analyser). We simply increment for each new request. + * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one + * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */ + static u32_t xid = 0xABCD0000; +#else + static u32_t xid; + static u8_t xid_initialised = 0; + if (!xid_initialised) { + xid = DHCP_GLOBAL_XID; + xid_initialised = !xid_initialised; + } +#endif + LWIP_ERROR("dhcp_create_request: netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_create_request: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); LWIP_ASSERT("dhcp_create_request: dhcp->p_out == NULL", dhcp->p_out == NULL); LWIP_ASSERT("dhcp_create_request: dhcp->msg_out == NULL", dhcp->msg_out == NULL); dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); if (dhcp->p_out == NULL) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_create_request(): could not allocate pbuf\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_create_request(): could not allocate pbuf\n")); return ERR_MEM; } - /* give unique transaction identifier to this request */ - dhcp->xid = xid++; + LWIP_ASSERT("dhcp_create_request: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries==0) + xid++; + dhcp->xid = xid; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, + ("transaction id xid(%"X32_F")\n", xid)); dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; @@ -1276,7 +1516,10 @@ static err_t dhcp_create_request(struct dhcp->msg_out->xid = htonl(dhcp->xid); dhcp->msg_out->secs = 0; dhcp->msg_out->flags = 0; - dhcp->msg_out->ciaddr.addr = netif->ip_addr.addr; + dhcp->msg_out->ciaddr.addr = 0; + if (dhcp->state==DHCP_BOUND || dhcp->state==DHCP_RENEWING || dhcp->state==DHCP_REBINDING) { + dhcp->msg_out->ciaddr.addr = netif->ip_addr.addr; + } dhcp->msg_out->yiaddr.addr = 0; dhcp->msg_out->siaddr.addr = 0; dhcp->msg_out->giaddr.addr = 0; @@ -1284,21 +1527,38 @@ static err_t dhcp_create_request(struct /* copy netif hardware address, pad with zeroes */ dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/; } - for (i = 0; i < DHCP_SNAME_LEN; i++) dhcp->msg_out->sname[i] = 0; - for (i = 0; i < DHCP_FILE_LEN; i++) dhcp->msg_out->file[i] = 0; + for (i = 0; i < DHCP_SNAME_LEN; i++) { + dhcp->msg_out->sname[i] = 0; + } + for (i = 0; i < DHCP_FILE_LEN; i++) { + dhcp->msg_out->file[i] = 0; + } dhcp->msg_out->cookie = htonl(0x63825363UL); dhcp->options_out_len = 0; /* fill options field with an incrementing array (for debugging purposes) */ - for (i = 0; i < DHCP_OPTIONS_LEN; i++) dhcp->msg_out->options[i] = i; + for (i = 0; i < DHCP_OPTIONS_LEN; i++) { + dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */ + } return ERR_OK; } -static void dhcp_delete_request(struct netif *netif) +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_delete_request(struct netif *netif) { - struct dhcp *dhcp = netif->dhcp; - LWIP_ASSERT("dhcp_free_msg: dhcp->p_out != NULL", dhcp->p_out != NULL); - LWIP_ASSERT("dhcp_free_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL); - pbuf_free(dhcp->p_out); + struct dhcp *dhcp; + LWIP_ERROR("dhcp_delete_request: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_delete_request: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_request: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_request: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + if (dhcp->p_out != NULL) { + pbuf_free(dhcp->p_out); + } dhcp->p_out = NULL; dhcp->msg_out = NULL; } @@ -1308,10 +1568,13 @@ static void dhcp_delete_request(struct n * * Adds the END option to the DHCP message, and if * necessary, up to three padding bytes. + * + * @param dhcp DHCP state structure */ - -static void dhcp_option_trailer(struct dhcp *dhcp) +static void +dhcp_option_trailer(struct dhcp *dhcp) { + LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;); LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; @@ -1327,7 +1590,7 @@ static void dhcp_option_trailer(struct d /** * Find the offset of a DHCP option inside the DHCP message. * - * @param client DHCP client + * @param dhcp DHCP client * @param option_type * * @return a byte offset into the UDP message where the option was found, or @@ -1347,14 +1610,14 @@ static u8_t *dhcp_get_option_ptr(struct /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ /* are the sname and/or file field overloaded with options? */ if (options[offset] == DHCP_OPTION_OVERLOAD) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("overloaded message detected\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded message detected\n")); /* skip option type and length */ offset += 2; overload = options[offset++]; } /* requested option found */ else if (options[offset] == option_type) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("option found at offset %"U16_F" in options\n", offset)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset %"U16_F" in options\n", offset)); return &options[offset]; /* skip option */ } else { @@ -1369,16 +1632,16 @@ static u8_t *dhcp_get_option_ptr(struct if (overload != DHCP_OVERLOAD_NONE) { u16_t field_len; if (overload == DHCP_OVERLOAD_FILE) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded file field\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); options = (u8_t *)&dhcp->msg_in->file; field_len = DHCP_FILE_LEN; } else if (overload == DHCP_OVERLOAD_SNAME) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded sname field\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); options = (u8_t *)&dhcp->msg_in->sname; field_len = DHCP_SNAME_LEN; /* TODO: check if else if () is necessary */ } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded sname and file field\n")); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); options = (u8_t *)&dhcp->msg_in->sname; field_len = DHCP_FILE_LEN + DHCP_SNAME_LEN; } @@ -1387,11 +1650,11 @@ static u8_t *dhcp_get_option_ptr(struct /* at least 1 byte to read and no end marker */ while ((offset < field_len) && (options[offset] != DHCP_OPTION_END)) { if (options[offset] == option_type) { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("option found at offset=%"U16_F"\n", offset)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset=%"U16_F"\n", offset)); return &options[offset]; /* skip option */ } else { - LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("skipping option %"U16_F"\n", options[offset])); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("skipping option %"U16_F"\n", options[offset])); /* skip option type */ offset++; offset += 1 + options[offset]; @@ -1399,7 +1662,7 @@ static u8_t *dhcp_get_option_ptr(struct } } } - return 0; + return NULL; } /** @@ -1410,12 +1673,14 @@ static u8_t *dhcp_get_option_ptr(struct * * @return byte value at the given address. */ -static u8_t dhcp_get_option_byte(u8_t *ptr) +static u8_t +dhcp_get_option_byte(u8_t *ptr) { LWIP_DEBUGF(DHCP_DEBUG, ("option byte value=%"U16_F"\n", (u16_t)(*ptr))); return *ptr; } +#if 0 /* currently unused */ /** * Return the 16-bit value of DHCP option data. * @@ -1424,7 +1689,8 @@ static u8_t dhcp_get_option_byte(u8_t *p * * @return byte value at the given address. */ -static u16_t dhcp_get_option_short(u8_t *ptr) +static u16_t +dhcp_get_option_short(u8_t *ptr) { u16_t value; value = *ptr++ << 8; @@ -1432,6 +1698,7 @@ static u16_t dhcp_get_option_short(u8_t LWIP_DEBUGF(DHCP_DEBUG, ("option short value=%"U16_F"\n", value)); return value; } +#endif /** * Return the 32-bit value of DHCP option data. diff --git a/packages/net/lwip_tcpip/current/src/core/dns.c b/packages/net/lwip_tcpip/current/src/core/dns.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/dns.c @@ -0,0 +1,980 @@ +/** + * @file + * DNS - host name to IP address resolver. + * + */ + +/** + + * This file implements a DNS host name to IP address resolver. + + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * + * DNS.C + * + * The lwIP DNS resolver functions are used to lookup a host name and + * map it to a numerical IP address. It maintains a list of resolved + * hostnames that can be queried with the dns_lookup() function. + * New hostnames can be resolved using the dns_query() function. + * + * The lwIP version of the resolver also adds a non-blocking version of + * gethostbyname() that will work with a raw API application. This function + * checks for an IP address string first and converts it if it is valid. + * gethostbyname() then does a dns_lookup() to see if the name is + * already in the table. If so, the IP is returned. If not, a query is + * issued and the function returns with a ERR_INPROGRESS status. The app + * using the dns client must then go into a waiting state. + * + * Once a hostname has been resolved (or found to be non-existent), + * the resolver code calls a specified callback function (which + * must be implemented by the module that uses the resolver). + */ + +/*----------------------------------------------------------------------------- + * RFC 1035 - Domain names - implementation and specification + * RFC 2181 - Clarifications to the DNS Specification + *----------------------------------------------------------------------------*/ + +/** @todo: define good default values (rfc compliance) */ +/** @todo: improve answer parsing, more checkings... */ +/** @todo: check RFC1035 - 7.3. Processing responses */ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/dns.h" + +#include + +/** DNS server IP address */ +#ifndef DNS_SERVER_ADDRESS +#define DNS_SERVER_ADDRESS inet_addr("208.67.222.222") /* resolver1.opendns.com */ +#endif + +/** DNS server port address */ +#ifndef DNS_SERVER_PORT +#define DNS_SERVER_PORT 53 +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#endif + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +/* DNS protocol states */ +#define DNS_STATE_UNUSED 0 +#define DNS_STATE_NEW 1 +#define DNS_STATE_ASKING 2 +#define DNS_STATE_DONE 3 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS message header */ +struct dns_hdr { + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u8_t flags1); + PACK_STRUCT_FIELD(u8_t flags2); + PACK_STRUCT_FIELD(u16_t numquestions); + PACK_STRUCT_FIELD(u16_t numanswers); + PACK_STRUCT_FIELD(u16_t numauthrr); + PACK_STRUCT_FIELD(u16_t numextrarr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_HDR 12 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS query message structure */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t class); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_QUERY 4 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS answer message structure */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t class); + PACK_STRUCT_FIELD(u32_t ttl); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_ANSWER 10 + +/** DNS table entry */ +struct dns_table_entry { + u8_t state; + u8_t numdns; + u8_t tmr; + u8_t retries; + u8_t seqno; + u8_t err; + u32_t ttl; + char name[DNS_MAX_NAME_LENGTH]; + struct ip_addr ipaddr; + /* pointer to callback on DNS query done */ + dns_found_callback found; + void *arg; +}; + +#if DNS_LOCAL_HOSTLIST +/** struct used for local host-list */ +struct local_hostlist_entry { + /** static hostname */ + const char *name; + /** static host address in network byteorder */ + u32_t addr; + struct local_hostlist_entry *next; +}; + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Local host-list. For hostnames in this list, no + * external name resolution is performed */ +static struct local_hostlist_entry *local_hostlist_dynamic; +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE +#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST +#define DNS_LOCAL_HOSTLIST_STORAGE_POST +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ +DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] + DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; + +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +static void dns_init_local(); +#endif /* DNS_LOCAL_HOSTLIST */ + + +/* forward declarations */ +static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static void dns_check_entries(void); + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcb; +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static struct ip_addr dns_servers[DNS_MAX_SERVERS]; + +#if (DNS_USES_STATIC_BUF == 1) +static u8_t dns_payload[DNS_MSG_SIZE]; +#endif /* (DNS_USES_STATIC_BUF == 1) */ + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void +dns_init() +{ + struct ip_addr dnsserver; + + /* initialize default DNS server address */ + dnsserver.addr = DNS_SERVER_ADDRESS; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ + if (dns_pcb == NULL) { + dns_pcb = udp_new(); + + if (dns_pcb != NULL) { + /* initialize DNS table not needed (initialized to zero since it is a + * global variable) */ + LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", + DNS_STATE_UNUSED == 0); + + /* initialize DNS client */ + udp_bind(dns_pcb, IP_ADDR_ANY, 0); + udp_recv(dns_pcb, dns_recv, NULL); + + /* initialize default DNS primary server */ + dns_setserver(0, &dnsserver); + } + } +#if DNS_LOCAL_HOSTLIST + dns_init_local(); +#endif +} + +/** + * Initialize one of the DNS servers. + * + * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS + * @param dnsserver IP address of the DNS server to set + */ +void +dns_setserver(u8_t numdns, struct ip_addr *dnsserver) +{ + if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && + (dnsserver != NULL) && (dnsserver->addr !=0 )) { + dns_servers[numdns] = (*dnsserver); + } +} + +/** + * Obtain one of the currently configured DNS server. + * + * @param numdns the index of the DNS server + * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS + * server has not been configured. + */ +struct ip_addr +dns_getserver(u8_t numdns) +{ + if (numdns < DNS_MAX_SERVERS) { + return dns_servers[numdns]; + } else { + return *IP_ADDR_ANY; + } +} + +/** + * The DNS resolver client timer - handle retries and timeouts and should + * be called every DNS_TMR_INTERVAL milliseconds (every second by default). + */ +void +dns_tmr(void) +{ + if (dns_pcb != NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); + } +} + +#if DNS_LOCAL_HOSTLIST +static void +dns_init_local() +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) + int i; + struct local_hostlist_entry *entry; + /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ + struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; + for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { + entry = mem_malloc(sizeof(struct local_hostlist_entry)); + LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); + if (entry != NULL) { + struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; + entry->name = init_entry->name; + entry->addr = init_entry->addr; + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ +} + +/** + * Scans the local host-list for a hostname. + * + * @param hostname Hostname to look for in the local host-list + * @return The first IP address for the hostname in the local host-list or + * INADDR_NONE if not found. + */ +static u32_t +dns_lookup_local(const char *hostname) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + while(entry != NULL) { + if(strcmp(entry->name, hostname) == 0) { + return entry->addr; + } + entry = entry->next; + } +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + int i; + for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) { + if(strcmp(local_hostlist_static[i].name, hostname) == 0) { + return local_hostlist_static[i].addr; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return INADDR_NONE; +} + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Remove all entries from the local host-list for a specific hostname + * and/or IP addess + * + * @param hostname hostname for which entries shall be removed from the local + * host-list + * @param addr address for which entries shall be removed from the local host-list + * @return the number of removed entries + */ +int +dns_local_removehost(const char *hostname, const struct ip_addr *addr) +{ + int removed = 0; + struct local_hostlist_entry *entry = local_hostlist_dynamic; + struct local_hostlist_entry *last_entry = NULL; + while (entry != NULL) { + if (((hostname == NULL) || !strcmp(entry->name, hostname)) && + ((addr == NULL) || (entry->addr == addr->addr))) { + struct local_hostlist_entry *free_entry; + if (last_entry != NULL) { + last_entry->next = entry->next; + } else { + local_hostlist_dynamic = entry->next; + } + free_entry = entry; + entry = entry->next; + mem_free(free_entry); + removed++; + } else { + last_entry = entry; + entry = entry->next; + } + } + return removed; +} + +/** + * Add a hostname/IP address pair to the local host-list. + * Duplicates are not checked. + * + * @param hostname hostname of the new entry + * @param addr IP address of the new entry + * @return ERR_OK if succeeded or ERR_MEM on memory error + */ +err_t +dns_local_addhost(const char *hostname, const struct ip_addr *addr) +{ + struct local_hostlist_entry *entry; + entry = mem_malloc(sizeof(struct local_hostlist_entry)); + if (entry == NULL) { + return ERR_MEM; + } + entry->name = hostname; + entry->addr = addr->addr; + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + return ERR_OK; +} +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** + * Look up a hostname in the array of known hostnames. + * + * @note This function only looks in the internal array of known + * hostnames, it does not send out a query for the hostname if none + * was found. The function dns_enqueue() can be used to send a query + * for a hostname. + * + * @param name the hostname to look up + * @return the hostname's IP address, as u32_t (instead of struct ip_addr to + * better check for failure: != INADDR_NONE) or INADDR_NONE if the hostname + * was not found in the cached dns_table. + */ +static u32_t +dns_lookup(const char *name) +{ + u8_t i; +#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) + u32_t addr; +#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ +#if DNS_LOCAL_HOSTLIST + if ((addr = dns_lookup_local(name)) != INADDR_NONE) { + return addr; + } +#endif /* DNS_LOCAL_HOSTLIST */ +#ifdef DNS_LOOKUP_LOCAL_EXTERN + if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != INADDR_NONE) { + return addr; + } +#endif /* DNS_LOOKUP_LOCAL_EXTERN */ + + /* Walk through name list, return entry if found. If not, return NULL. */ + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (strcmp(name, dns_table[i].name) == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + return dns_table[i].ipaddr.addr; + } + } + + return INADDR_NONE; +} + +#if DNS_DOES_NAME_CHECK +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current dns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the dns_table + * @param response encoded hostname in the DNS response + * @return 0: names equal; 1: names differ + */ +static u8_t +dns_compare_name(unsigned char *query, unsigned char *response) +{ + unsigned char n; + + do { + n = *response++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != (*response)) { + return 1; + } + ++response; + ++query; + --n; + }; + ++query; + } + } while (*response != 0); + + return 0; +} +#endif /* DNS_DOES_NAME_CHECK */ + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param query encoded DNS name in the DNS server response + * @return end of the name + */ +static unsigned char * +dns_parse_name(unsigned char *query) +{ + unsigned char n; + + do { + n = *query++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + ++query; + --n; + }; + } + } while (*query != 0); + + return query + 1; +} + +/** + * Send a DNS query packet. + * + * @param numdns index of the DNS server in the dns_servers table + * @param name hostname to query + * @param id index of the hostname in dns_table, used as transaction ID in the + * DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t +dns_send(u8_t numdns, const char* name, u8_t id) +{ + err_t err; + struct dns_hdr *hdr; + struct dns_query qry; + struct pbuf *p; + char *query, *nptr; + const char *pHostname; + u8_t n; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(numdns), name)); + LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); + LWIP_ASSERT("dns server has no IP address set", dns_servers[numdns].addr != 0); + + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct dns_hdr*)p->payload; + memset(hdr, 0, SIZEOF_DNS_HDR); + hdr->id = htons(id); + hdr->flags1 = DNS_FLAG1_RD; + hdr->numquestions = htons(1); + query = (char*)hdr + SIZEOF_DNS_HDR; + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while(*pHostname != 0); + *query++='\0'; + + /* fill dns query */ + qry.type = htons(DNS_RRTYPE_A); + qry.class = htons(DNS_RRCLASS_IN); + MEMCPY( query, &qry, SIZEOF_DNS_QUERY); + + /* resize pbuf to the exact dns query */ + pbuf_realloc(p, (query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))); + + /* connect to the server for faster receiving */ + udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); + /* send dns packet */ + err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +/** + * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. + * Check an entry in the dns_table: + * - send out query for new entries + * - retry old pending entries on timeout (also with different servers) + * - remove completed entries from the table if their TTL has expired + * + * @param i index of the dns_table entry to check + */ +static void +dns_check_entry(u8_t i) +{ + struct dns_table_entry *pEntry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch(pEntry->state) { + + case DNS_STATE_NEW: { + /* initialize new entry */ + pEntry->state = DNS_STATE_ASKING; + pEntry->numdns = 0; + pEntry->tmr = 1; + pEntry->retries = 0; + + /* send DNS packet for this entry */ + dns_send(pEntry->numdns, pEntry->name, i); + break; + } + + case DNS_STATE_ASKING: { + if (--pEntry->tmr == 0) { + if (++pEntry->retries == DNS_MAX_RETRIES) { + if ((pEntry->numdns+1numdns+1].addr!=0)) { + /* change of server */ + pEntry->numdns++; + pEntry->tmr = 1; + pEntry->retries = 0; + break; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); + /* call specified callback function if provided */ + if (pEntry->found) + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + break; + } + } + + /* wait longer for the next retry */ + pEntry->tmr = pEntry->retries; + + /* send DNS packet for this entry */ + dns_send(pEntry->numdns, pEntry->name, i); + } + break; + } + + case DNS_STATE_DONE: { + /* if the time to live is nul */ + if (--pEntry->ttl == 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + } + break; + } + case DNS_STATE_UNUSED: + /* nothing to do */ + break; + default: + LWIP_ASSERT("unknown dns_table entry state:", 0); + break; + } +} + +/** + * Call dns_check_entry for each entry in dns_table - check all entries. + */ +static void +dns_check_entries(void) +{ + u8_t i; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + dns_check_entry(i); + } +} + +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + * + * @params see udp.h + */ +static void +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + u8_t i; + char *pHostname; + struct dns_hdr *hdr; + struct dns_answer ans; + struct dns_table_entry *pEntry; + u8_t nquestions, nanswers; +#if (DNS_USES_STATIC_BUF == 0) + u8_t dns_payload[DNS_MSG_SIZE]; +#endif /* (DNS_USES_STATIC_BUF == 0) */ +#if (DNS_USES_STATIC_BUF == 2) + u8_t* dns_payload; +#endif /* (DNS_USES_STATIC_BUF == 2) */ + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + /* is the dns message too big ? */ + if (p->tot_len > DNS_MSG_SIZE) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); + /* free pbuf and return */ + goto memerr1; + } + + /* is the dns message big enough ? */ + if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr1; + } + +#if (DNS_USES_STATIC_BUF == 2) + dns_payload = mem_malloc(p->tot_len); + if (dns_payload == NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: mem_malloc error\n")); + /* free pbuf and return */ + goto memerr1; + } +#endif /* (DNS_USES_STATIC_BUF == 2) */ + + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { + /* The ID in the DNS header should be our entry into the name table. */ + hdr = (struct dns_hdr*)dns_payload; + i = htons(hdr->id); + if (i < DNS_TABLE_SIZE) { + pEntry = &dns_table[i]; + if(pEntry->state == DNS_STATE_ASKING) { + /* This entry is now completed. */ + pEntry->state = DNS_STATE_DONE; + pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; + + /* We only care about the question(s) and the answers. The authrr + and the extrarr are simply discarded. */ + nquestions = htons(hdr->numquestions); + nanswers = htons(hdr->numanswers); + + /* Check for error. If so, call callback to inform. */ + if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + +#if DNS_DOES_NAME_CHECK + /* Check if the name in the "question" part match with the name in the entry. */ + if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } +#endif /* DNS_DOES_NAME_CHECK */ + + /* Skip the name in the "question" part */ + pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY; + + while(nanswers > 0) { + /* skip answer resource record's host name */ + pHostname = (char *) dns_parse_name((unsigned char *)pHostname); + + /* Check for IP address type and Internet class. Others are discarded. */ + MEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); + if((ntohs(ans.type) == DNS_RRTYPE_A) && (ntohs(ans.class) == DNS_RRCLASS_IN) && (ntohs(ans.len) == sizeof(struct ip_addr)) ) { + /* read the answer resource record's TTL, and maximize it if needed */ + pEntry->ttl = ntohl(ans.ttl); + if (pEntry->ttl > DNS_MAX_TTL) { + pEntry->ttl = DNS_MAX_TTL; + } + /* read the IP address after answer resource record's header */ + MEMCPY( &(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(struct ip_addr)); + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + /* call specified callback function if provided */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); + } + /* deallocate memory and return */ + goto memerr2; + } else { + pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len); + } + --nanswers; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + } + } + + /* deallocate memory and return */ + goto memerr2; + +responseerr: + /* ERROR: call specified callback function with NULL as name to indicate an error */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + } + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + +memerr2: +#if (DNS_USES_STATIC_BUF == 2) + /* free dns buffer */ + mem_free(dns_payload); +#endif /* (DNS_USES_STATIC_BUF == 2) */ + +memerr1: + /* free pbuf */ + pbuf_free(p); + return; +} + +/** + * Queues a new hostname to resolve and sends out a DNS query for that hostname + * + * @param name the hostname that is to be queried + * @param found a callback founction to be called on success, failure or timeout + * @param callback_arg argument to pass to the callback function + * @return @return a err_t return code. + */ +static err_t +dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *pEntry = NULL; + + /* search an unused entry, or the oldest one */ + lseq = lseqi = 0; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + pEntry = &dns_table[i]; + /* is it an unused entry ? */ + if (pEntry->state == DNS_STATE_UNUSED) + break; + + /* check if this is the oldest completed entry */ + if (pEntry->state == DNS_STATE_DONE) { + if ((dns_seqno - pEntry->seqno) > lseq) { + lseq = dns_seqno - pEntry->seqno; + lseqi = i; + } + } + } + + /* if we don't have found an unused entry, use the oldest completed one */ + if (i == DNS_TABLE_SIZE) { + if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { + /* no entry can't be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); + return ERR_MEM; + } else { + /* use the oldest completed one */ + i = lseqi; + pEntry = &dns_table[i]; + } + } + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + pEntry->state = DNS_STATE_NEW; + pEntry->seqno = dns_seqno++; + pEntry->found = found; + pEntry->arg = callback_arg; + strcpy(pEntry->name, name); + + /* force to send query without waiting timer */ + dns_check_entry(i); + + /* dns query is enqueued */ + return ERR_INPROGRESS; +} + +/** + * Resolve a hostname (string) into an IP address. + * NON-BLOCKING callback version for use with raw API!!! + * + * Returns immediately with one of err_t return codes: + * - ERR_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ERR_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a struct ip_addr where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @return a err_t return code. + */ +err_t +dns_gethostbyname(const char *hostname, struct ip_addr *addr, dns_found_callback found, + void *callback_arg) +{ + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((dns_pcb == NULL) || (addr == NULL) || + (!hostname) || (!hostname[0]) || + (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { + return ERR_VAL; + } + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname,"localhost")==0) { + addr->addr = htonl(INADDR_LOOPBACK); + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK + * already have this address cached? */ + if (((addr->addr = inet_addr(hostname)) != INADDR_NONE) || + ((addr->addr = dns_lookup(hostname)) != INADDR_NONE)) { + return ERR_OK; + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/packages/net/lwip_tcpip/current/src/core/init.c b/packages/net/lwip_tcpip/current/src/core/init.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/init.c @@ -0,0 +1,274 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/snmp_msg.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) + #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && ARP_QUEUEING) + #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_IGMP) + #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) + #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_WND > 0xffff)) + #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) + #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +//#if (PPP_SUPPORT && (NO_SYS==1)) +// #error "If you want to use PPP, you have to define NO_SYS=0 in your lwipopts.h" +//#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +#if (!LWIP_NETCONN && LWIP_SOCKET) + #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0)) + #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if ((NO_SYS==0) && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT))) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif +#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) + #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" +#endif +#if (TCP_QUEUE_OOSEQ && !LWIP_TCP) + #error "TCP_QUEUE_OOSEQ requires LWIP_TCP" +#endif +#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) + #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" +#endif +#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT + #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on" +#endif + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef MEMP_NUM_API_MSG + #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif +#if SO_REUSE +/* I removed the lot since this was an ugly hack. It broke the raw-API. + It also came with many ugly goto's, Christiaan Simons. */ + #error "SO_REUSE currently unavailable, this was a hack" +#endif + +#ifdef LWIP_DEBUG +static void +lwip_sanity_check(void) +{ + /* Warnings */ +#if LWIP_NETCONN + if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n")); +#endif /* LWIP_NETCONN */ +#if LWIP_TCP + if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n")); + if (TCP_SND_BUF < 2 * TCP_MSS) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly\n")); + if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS))) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n")); + if (TCP_SNDLOWAT > TCP_SND_BUF) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than or equal to TCP_SND_BUF.\n")); + if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n")); + if (TCP_WND < TCP_MSS) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n")); +#endif /* LWIP_TCP */ +} +#else /* LWIP_DEBUG */ +#define lwip_sanity_check() +#endif /* LWIP_DEBUG */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Sanity check user-configurable values */ + lwip_sanity_check(); + + /* Modules initialization */ + stats_init(); + sys_init(); + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); +#if LWIP_SOCKET + lwip_socket_init(); +#endif /* LWIP_SOCKET */ + ip_init(); +#if LWIP_ARP + etharp_init(); +#endif /* LWIP_ARP */ +#if LWIP_RAW + raw_init(); +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); +#endif /* LWIP_UDP */ +#if LWIP_TCP + tcp_init(); +#endif /* LWIP_TCP */ +#if LWIP_SNMP + snmp_init(); +#endif /* LWIP_SNMP */ +#if LWIP_AUTOIP + autoip_init(); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + igmp_init(); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); +#endif /* LWIP_DNS */ +} diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/autoip.c b/packages/net/lwip_tcpip/current/src/core/ipv4/autoip.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/autoip.c @@ -0,0 +1,497 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +/******************************************************************************* + * USAGE: + * + * define LWIP_AUTOIP 1 in your lwipopts.h + * + * If you don't use tcpip.c (so, don't call, you don't call tcpip_init): + * - First, call autoip_init(). + * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces, + * that should be defined in autoip.h. + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#include +#include + +/* 169.254.0.0 */ +#define AUTOIP_NET 0xA9FE0000 +/* 169.254.1.0 */ +#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100) +/* 169.254.254.255 */ +#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF) + + +/** Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif->autoip?netif->autoip->tried_llipaddr:0)) +#endif /* LWIP_AUTOIP_RAND */ + +/** + * Macro that generates the initial IP address to be tried by AUTOIP. + * If you want to override this, define it to something else in lwipopts.h. + */ +#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR +#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ + htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \ + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) +#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ + +/* static functions */ +static void autoip_handle_arp_conflict(struct netif *netif); + +/* creates a pseudo random LL IP-Address for a network interface */ +static void autoip_create_addr(struct netif *netif, struct ip_addr *ipaddr); + +/* sends an ARP probe */ +static err_t autoip_arp_probe(struct netif *netif); + +/* sends an ARP announce */ +static err_t autoip_arp_announce(struct netif *netif); + +/* configure interface for use with current LL IP-Address */ +static err_t autoip_bind(struct netif *netif); + +/* start sending probes for llipaddr */ +static void autoip_start_probing(struct netif *netif); + +/** + * Initialize this module + */ +void +autoip_init(void) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n")); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + /* Somehow detect if we are defending or retreating */ + unsigned char defend = 1; /* tbd */ + + if(defend) { + if(netif->autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last + * DEFEND_INTERVAL seconds + */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* TODO: close all TCP sessions */ + autoip_start(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we do not defend, retreating\n")); + /* TODO: close all TCP sessions */ + autoip_start(netif); + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param ipaddr ip address to initialize + */ +static void +autoip_create_addr(struct netif *netif, struct ip_addr *ipaddr) +{ + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities */ + + u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); + addr += netif->autoip->tried_llipaddr; + addr = AUTOIP_NET | (addr & 0xffff); + /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ + + if (addr < AUTOIP_RANGE_START) { + addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + if (addr > AUTOIP_RANGE_END) { + addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && + (addr <= AUTOIP_RANGE_END)); + ipaddr->addr = htonl(addr); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", 0x%08"X32_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), (u32_t)(ipaddr->addr))); +} + +/** + * Sends an ARP probe from a network interface + * + * @param netif network interface used to send the probe + */ +static err_t +autoip_arp_probe(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + struct ip_addr sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" 0x%08"X32_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, autoip->llipaddr.addr)); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_ipaddr(netif, &autoip->llipaddr); + netif_set_netmask(netif, &sn_mask); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + return ERR_OK; +} + +/** + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + err_t result = ERR_OK; + + if(netif_is_up(netif)) { + netif_set_down(netif); + } + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + netif->ip_addr.addr = 0; + netif->netmask.addr = 0; + netif->gw.addr = 0; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if(autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = mem_malloc(sizeof(struct autoip)); + if(autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + memset( autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif->autoip = autoip; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + memset(&autoip->llipaddr, 0, sizeof(struct ip_addr)); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip->tried_llipaddr++; + autoip_start_probing(netif); + + return result; +} + +static void +autoip_start_probing(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + + /* time to wait to first probe, this is randomly + * choosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * accquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + if(autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Handle a possible change in the network configuration. + * + * If there is an AutoIP address configured, take the interface down + * and begin probing with the same address. + */ +void +autoip_network_changed(struct netif *netif) +{ + if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) { + netif_set_down(netif); + autoip_start_probing(netif); + } +} + +/** + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + netif->autoip->state = AUTOIP_STATE_OFF; + netif_set_down(netif); + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on AutoIP configured interfaces */ + if (netif->autoip != NULL) { + if(netif->autoip->lastconflict > 0) { + netif->autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(netif->autoip->state), netif->autoip->ttw)); + + switch(netif->autoip->state) { + case AUTOIP_STATE_PROBING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num >= PROBE_NUM) { + netif->autoip->state = AUTOIP_STATE_ANNOUNCING; + netif->autoip->sent_num = 0; + netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + } else { + autoip_arp_probe(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() PROBING Sent Probe\n")); + netif->autoip->sent_num++; + /* calculate time to wait to next probe */ + netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num == 0) { + /* We are here the first time, so we waited ANNOUNCE_WAIT seconds + * Now we can bind to an IP address and use it. + * + * autoip_bind calls netif_set_up. This triggers a gratuitous ARP + * which counts as an announcement. + */ + autoip_bind(netif); + } else { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() ANNOUNCING Sent Announce\n")); + } + netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + netif->autoip->sent_num++; + + if(netif->autoip->sent_num >= ANNOUNCE_NUM) { + netif->autoip->state = AUTOIP_STATE_BOUND; + netif->autoip->sent_num = 0; + netif->autoip->ttw = 0; + } + } + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_arp_input. + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n")); + if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + struct ip_addr sipaddr, dipaddr; + struct eth_addr netifaddr; + netifaddr.addr[0] = netif->hwaddr[0]; + netifaddr.addr[1] = netif->hwaddr[1]; + netifaddr.addr[2] = netif->hwaddr[2]; + netifaddr.addr[3] = netif->hwaddr[3]; + netifaddr.addr[4] = netif->hwaddr[4]; + netifaddr.addr[5] = netif->hwaddr[5]; + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); + SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr)); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && + (netif->autoip->sent_num == 0))) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) || + (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_start(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +#endif /* LWIP_AUTOIP */ diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/icmp.c b/packages/net/lwip_tcpip/current/src/core/ipv4/icmp.c --- a/packages/net/lwip_tcpip/current/src/core/ipv4/icmp.c +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/icmp.c @@ -1,3 +1,9 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -33,25 +39,52 @@ /* Some ICMP messages should be passed to the transport protocols. This is not implemented. */ -#include +#include "lwip/opt.h" -#include "lwip/opt.h" +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + #include "lwip/icmp.h" #include "lwip/inet.h" +#include "lwip/inet_chksum.h" #include "lwip/ip.h" #include "lwip/def.h" #include "lwip/stats.h" #include "lwip/snmp.h" +#include + +/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be + * used to modify and send a response packet (and to 1 if this is not the case, + * e.g. when link header is stripped of when receiving) */ +#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the ip header + * @param inp the netif on which this packet was received + */ void icmp_input(struct pbuf *p, struct netif *inp) { u8_t type; +#ifdef LWIP_DEBUG u8_t code; +#endif /* LWIP_DEBUG */ struct icmp_echo_hdr *iecho; struct ip_hdr *iphdr; struct ip_addr tmpaddr; - u16_t hlen; + s16_t hlen; ICMP_STATS_INC(icmp.recv); snmp_inc_icmpinmsgs(); @@ -59,35 +92,46 @@ icmp_input(struct pbuf *p, struct netif iphdr = p->payload; hlen = IPH_HL(iphdr) * 4; - if (pbuf_header(p, -((s16_t)hlen)) || (p->tot_len < sizeof(u16_t)*2)) { + if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) { LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); - pbuf_free(p); - ICMP_STATS_INC(icmp.lenerr); - snmp_inc_icmpinerrors(); - return; + goto lenerr; } type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ switch (type) { case ICMP_ECHO: - /* broadcast or multicast destination address? */ - if (ip_addr_isbroadcast(&iphdr->dest, inp) || ip_addr_ismulticast(&iphdr->dest)) { - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); - ICMP_STATS_INC(icmp.err); - pbuf_free(p); - return; +#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING + { + int accepted = 1; +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip_addr_ismulticast(&iphdr->dest)) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(&iphdr->dest, inp)) { + accepted = 0; + } +#endif /* LWIP_BROADCAST_PING */ + /* broadcast or multicast destination address not acceptd? */ + if (!accepted) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } } +#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); if (p->tot_len < sizeof(struct icmp_echo_hdr)) { LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); - pbuf_free(p); - ICMP_STATS_INC(icmp.lenerr); - snmp_inc_icmpinerrors(); - - return; + goto lenerr; } - iecho = p->payload; if (inet_chksum_pbuf(p) != 0) { LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); pbuf_free(p); @@ -95,6 +139,52 @@ icmp_input(struct pbuf *p, struct netif snmp_inc_icmpinerrors(); return; } +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* switch p->payload to ip header */ + if (pbuf_header(p, hlen)) { + LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); + goto memerr; + } + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", + (r->len >= hlen + sizeof(struct icmp_echo_hdr))); + /* copy the whole packet including ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); + goto memerr; + } + iphdr = r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header */ + if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + } +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = p->payload; tmpaddr.addr = iphdr->src.addr; iphdr->src.addr = iphdr->dest.addr; iphdr->dest.addr = tmpaddr.addr; @@ -105,65 +195,109 @@ icmp_input(struct pbuf *p, struct netif } else { iecho->chksum += htons(ICMP_ECHO << 8); } + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + ICMP_STATS_INC(icmp.xmit); /* increase number of messages attempted to send */ snmp_inc_icmpoutmsgs(); /* increase number of echo replies attempted to send */ snmp_inc_icmpoutechoreps(); - pbuf_header(p, hlen); - ip_output_if(p, &(iphdr->src), IP_HDRINCL, - IPH_TTL(iphdr), 0, IP_PROTO_ICMP, inp); + if(pbuf_header(p, hlen)) { + LWIP_ASSERT("Can't move over header in packet", 0); + } else { + err_t ret; + ret = ip_output_if(p, &(iphdr->src), IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret)); + } + } break; default: - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", (s16_t)type, (s16_t)code)); + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); ICMP_STATS_INC(icmp.proterr); ICMP_STATS_INC(icmp.drop); } pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +memerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + snmp_inc_icmpinerrors(); + return; +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ } +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) { + icmp_send_response(p, ICMP_DUR, t); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + icmp_send_response(p, ICMP_TE, t); +} + +#endif /* IP_FORWARD || IP_REASSEMBLY */ + +/** + * Send an icmp packet in response to an incoming packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param type Type of the ICMP header + * @param code Code of the ICMP header + */ +static void +icmp_send_response(struct pbuf *p, u8_t type, u8_t code) +{ struct pbuf *q; struct ip_hdr *iphdr; - struct icmp_dur_hdr *idur; - - q = pbuf_alloc(PBUF_IP, 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); - - memcpy((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8); + /* we can use the echo header here */ + struct icmp_echo_hdr *icmphdr; - /* calculate checksum */ - idur->chksum = 0; - idur->chksum = inet_chksum(idur, q->len); - ICMP_STATS_INC(icmp.xmit); - /* increase number of messages attempted to send */ - snmp_inc_icmpoutmsgs(); - /* increase number of destination unreachable messages attempted to send */ - snmp_inc_icmpoutdestunreachs(); - - ip_output(q, NULL, &(iphdr->src), - ICMP_TTL, 0, IP_PROTO_ICMP); - pbuf_free(q); -} - -#if IP_FORWARD -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_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); iphdr = p->payload; LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); @@ -172,31 +306,26 @@ icmp_time_exceeded(struct pbuf *p, enum ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); LWIP_DEBUGF(ICMP_DEBUG, ("\n")); - tehdr = q->payload; - ICMPH_TYPE_SET(tehdr, ICMP_TE); - ICMPH_CODE_SET(tehdr, t); + icmphdr = q->payload; + icmphdr->type = type; + icmphdr->code = code; + icmphdr->id = 0; + icmphdr->seqno = 0; /* copy fields from original packet */ - memcpy((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8); + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); /* calculate checksum */ - tehdr->chksum = 0; - tehdr->chksum = inet_chksum(tehdr, q->len); + icmphdr->chksum = 0; + icmphdr->chksum = inet_chksum(icmphdr, q->len); ICMP_STATS_INC(icmp.xmit); /* increase number of messages attempted to send */ snmp_inc_icmpoutmsgs(); /* increase number of destination unreachable messages attempted to send */ snmp_inc_icmpouttimeexcds(); - ip_output(q, NULL, &(iphdr->src), - ICMP_TTL, 0, IP_PROTO_ICMP); + ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP); pbuf_free(q); } -#endif /* IP_FORWARD */ - - - - - - - +#endif /* LWIP_ICMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/igmp.c b/packages/net/lwip_tcpip/current/src/core/ipv4/igmp.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/igmp.c @@ -0,0 +1,757 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * 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 CITEL Technologies Ltd 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 CITEL TECHNOLOGIES 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 CITEL TECHNOLOGIES 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 a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#include "string.h" + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +static struct igmp_group* igmp_group_list; +static struct ip_addr allsystems; +static struct ip_addr allrouters; + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +#ifdef LWIP_DEBUG +/** + * Dump global IGMP groups list + */ +void +igmp_dump_group_list() +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->interface)); + group = group->next; + } + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); +} +#else +#define igmp_dump_group_list() +#endif /* LWIP_DEBUG */ + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, &allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter( netif, &allsystems, IGMP_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + struct igmp_group *prev = NULL; + struct igmp_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->interface == netif) { + /* is it the first group of the list? */ + if (group == igmp_group_list) { + igmp_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups( struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); + + while (group != NULL) { + if (group->interface == netif) { + igmp_delaying_member( group, IGMP_JOIN_DELAYING_MEMBER_TMR); + } + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, struct ip_addr *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +struct igmp_group * +igmp_lookup_group(struct netif *ifp, struct ip_addr *addr) +{ + struct igmp_group *group = igmp_group_list; + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->interface = ifp; + ip_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = igmp_group_list; + + igmp_group_list = group; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +err_t +igmp_remove_group(struct igmp_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (igmp_group_list == group) { + igmp_group_list = group->next; + } else { + /* look for group further down the list */ + struct igmp_group *tmpGroup; + for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the ip header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, struct ip_addr *dest) +{ + struct ip_hdr * iphdr; + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + iphdr = p->payload; + if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the incoming IP address! */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: { + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip_addr_cmp(dest, &allsystems)) && (igmp->igmp_group_address.addr == 0)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.v1_rxed); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } + + IGMP_STATS_INC(igmp.group_query_rxed); + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->interface == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + igmp_delaying_member( groupref, igmp->igmp_maxresp); + } + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (group->group_address.addr != 0) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + if (ip_addr_cmp (dest, &allsystems)) { + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-lookfor the group since we used dest last time */ + group = igmp_lookfor_group(inp, &igmp->igmp_group_address); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.unicast_query); + igmp_delaying_member( group, igmp->igmp_maxresp); + } + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + + IGMP_STATS_INC(igmp.report_rxed); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + } + default: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", + igmp->igmp_msgtype, group->group_state, &group, group->interface)); + break; + } + } + + pbuf_free(p); + return; +} + +/** + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.join_sent); + igmp_send(group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + err = ERR_OK; + } else { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); + return ERR_MEM; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); + IGMP_STATS_INC(igmp.leave_sent); + igmp_send(group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* Free the group */ + igmp_remove_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } else { + /* It's not a fatal error on "leavegroup" */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if (group->timer != 0) { + group->timer -= 1; + if (group->timer == 0) { + igmp_timeout(group); + } + } + group = group->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +void +igmp_timeout(struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->interface)); + + igmp_send(group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /** + * @todo Important !! this should be random 0 -> max_time. Find out how to do this + */ + group->timer = max_time; +} + +/** + * Stop a timer for an igmp_group + * + * @param group the igmp_group for which to stop the timer + */ +void +igmp_stop_timer(struct igmp_group *group) +{ + group->timer = 0; +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +void +igmp_delaying_member( struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || + ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && (maxresp > group->timer))) { + igmp_start_timer(group, (maxresp)/2); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * 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. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +err_t +igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = htons (ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + return ip_output_if_opt(p, src, dest, ttl, 0, proto, netif, ra, ROUTER_ALERTLEN); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + struct ip_addr src = {0}; + struct ip_addr* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_set(&src, &((group->interface)->ip_addr)); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + IGMP_STATS_INC(igmp.report_sent); + ip_addr_set(&(igmp->igmp_group_address), &(group->group_address)); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip_addr_set(&(igmp->igmp_group_address), &(group->group_address)); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN); + + igmp_ip_output_if(p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + } +} + +#endif /* LWIP_IGMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/inet.c b/packages/net/lwip_tcpip/current/src/core/ipv4/inet.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/inet.c @@ -0,0 +1,278 @@ +/** + * @file + * Functions common to all TCP/IPv4 modules, such as the byte order functions. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet.h" + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +inet_addr(const char *cp) +{ + struct in_addr val; + + if (inet_aton(cp, &val)) { + return (val.s_addr); + } + return (INADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +inet_aton(const char *cp, struct in_addr *addr) +{ + u32_t val; + u8_t base; + char c; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else + base = 8; + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (int)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + switch (pp - parts + 1) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffUL) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +inet_ntoa(struct in_addr addr) +{ + static char str[16]; + u32_t s_addr = addr.s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + + rp = str; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) + *rp++ = inv[i]; + *rp++ = '.'; + ap++; + } + *--rp = 0; + return str; +} + +/** + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * #define LWIP_PLATFORM_BYTESWAP 1 + * #define LWIP_PLATFORM_HTONS(x) + * #define LWIP_PLATFORM_HTONL(x) + * + * Note ntohs() and ntohl() are merely references to the htonx counterparts. + */ + +#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) + +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +/** + * Convert an u16_t from network- to host byte order. + * + * @param n u16_t in network byte order + * @return n in host byte order + */ +u16_t +ntohs(u16_t n) +{ + return htons(n); +} + +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); +} + +/** + * Convert an u32_t from network- to host byte order. + * + * @param n u32_t in network byte order + * @return n in host byte order + */ +u32_t +ntohl(u32_t n) +{ + return htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/inet_chksum.c b/packages/net/lwip_tcpip/current/src/core/ipv4/inet_chksum.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/inet_chksum.c @@ -0,0 +1,438 @@ +/** + * @file + * Incluse internet checksum functions. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/inet.h" + +#include + +/* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * #define LWIP_CHKSUM + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 1 +# endif +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +/** Like the name says... */ +#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) +/* little endian and PLATFORM_BYTESWAP defined */ +#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w) +#else +/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */ +#define SWAP_BYTES_IN_WORD(w) ((w & 0xff) << 8) | ((w & 0xff00) >> 8) +#endif + +/** Split an u32_t in two u16_ts and add them up */ +#define FOLD_U32T(u) ((u >> 16) + (u & 0x0000ffffUL)) + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +static u16_t +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ + +static u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((u32_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps;; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +static u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((u32_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((u32_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return sum; +} +#endif + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +/* Currently only used by UDPLITE, although this could change in the future. */ +#if LWIP_UDPLITE +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + u16_t chklen; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} +#endif /* LWIP_UDPLITE */ + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + return ~LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/ip.c b/packages/net/lwip_tcpip/current/src/core/ipv4/ip.c --- a/packages/net/lwip_tcpip/current/src/core/ipv4/ip.c +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/ip.c @@ -1,10 +1,11 @@ -/* @file - * - * This is the IP layer implementation for incoming and outgoing IP traffic. +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. * * @see ip_frag.c * */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -38,45 +39,45 @@ */ #include "lwip/opt.h" - +#include "lwip/ip.h" #include "lwip/def.h" #include "lwip/mem.h" -#include "lwip/ip.h" #include "lwip/ip_frag.h" #include "lwip/inet.h" +#include "lwip/inet_chksum.h" #include "lwip/netif.h" #include "lwip/icmp.h" +#include "lwip/igmp.h" #include "lwip/raw.h" #include "lwip/udp.h" #include "lwip/tcp.h" - +#include "lwip/snmp.h" +#include "lwip/dhcp.h" #include "lwip/stats.h" - #include "arch/perf.h" -#include "lwip/snmp.h" -#if LWIP_DHCP -# include "lwip/dhcp.h" -#endif /* LWIP_DHCP */ - +#include /** - * Initializes the IP layer. + * The interface that provided the packet for the current callback + * invocation. */ +struct netif *current_netif; -void -ip_init(void) -{ - /* no initializations as of yet */ -} +/** + * Header of the input packet currently being processed. + */ +const struct ip_hdr *current_header; /** * 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. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest */ - struct netif * ip_route(struct ip_addr *dest) { @@ -85,22 +86,34 @@ ip_route(struct ip_addr *dest) /* iterate through netifs */ for(netif = netif_list; netif != NULL; netif = netif->next) { /* network mask matches? */ - if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { - /* return netif on which to forward IP packet */ - return netif; + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } } } + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } /* no matching netif found, use default netif */ return netif_default; } + #if 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. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + * @return the netif on which the packet was sent (NULL if it wasn't sent) */ - static struct netif * ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) { @@ -112,14 +125,14 @@ ip_forward(struct pbuf *p, struct ip_hdr if (netif == NULL) { LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%"X32_F" found\n", iphdr->dest.addr)); - snmp_inc_ipnoroutes(); + snmp_inc_ipoutnoroutes(); return (struct netif *)NULL; } /* Do not forward packets onto the same network interface on which * they arrived. */ if (netif == inp) { LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); - snmp_inc_ipnoroutes(); + snmp_inc_ipoutnoroutes(); return (struct netif *)NULL; } @@ -127,11 +140,13 @@ ip_forward(struct pbuf *p, struct ip_hdr IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); /* send ICMP if TTL == 0 */ if (IPH_TTL(iphdr) == 0) { + snmp_inc_ipinhdrerrors(); +#if LWIP_ICMP /* Don't send ICMP messages in response to ICMP messages */ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { icmp_time_exceeded(p, ICMP_TE_TTL); - snmp_inc_icmpouttimeexcds(); } +#endif /* LWIP_ICMP */ return (struct netif *)NULL; } @@ -147,7 +162,7 @@ ip_forward(struct pbuf *p, struct ip_hdr IP_STATS_INC(ip.fw); IP_STATS_INC(ip.xmit); - snmp_inc_ipforwdatagrams(); + snmp_inc_ipforwdatagrams(); PERF_STOP("ip_forward"); /* transmit pbuf on chosen interface */ @@ -165,15 +180,21 @@ ip_forward(struct pbuf *p, struct ip_hdr * * Finally, the packet is sent to the upper layer protocol input function. * - * - * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) */ - err_t -ip_input(struct pbuf *p, struct netif *inp) { +ip_input(struct pbuf *p, struct netif *inp) +{ struct ip_hdr *iphdr; struct netif *netif; - u16_t iphdrlen; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if LWIP_DHCP + int check_ip_src=1; +#endif /* LWIP_DHCP */ IP_STATS_INC(ip.recv); snmp_inc_ipinreceives(); @@ -181,23 +202,34 @@ ip_input(struct pbuf *p, struct netif *i /* identify the IP header */ iphdr = p->payload; if (IPH_V(iphdr) != 4) { - LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.err); IP_STATS_INC(ip.drop); - snmp_inc_ipunknownprotos(); + snmp_inc_ipinhdrerrors(); return ERR_OK; } + /* obtain IP header length in number of 32-bit words */ - iphdrlen = IPH_HL(iphdr); + iphdr_hlen = IPH_HL(iphdr); /* calculate IP header length in bytes */ - iphdrlen *= 4; + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = ntohs(IPH_LEN(iphdr)); - /* header length exceeds first pbuf length? */ - if (iphdrlen > p->len) { - LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet droppped.\n", - iphdrlen, p->len)); + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { + if (iphdr_hlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + } + if (iphdr_len > p->tot_len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_len, p->tot_len)); + } /* free (drop) packet pbufs */ pbuf_free(p); IP_STATS_INC(ip.lenerr); @@ -208,45 +240,70 @@ ip_input(struct pbuf *p, struct netif *i /* verify checksum */ #if CHECKSUM_CHECK_IP - if (inet_chksum(iphdr, iphdrlen) != 0) { + if (inet_chksum(iphdr, iphdr_hlen) != 0) { - LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdrlen))); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.chkerr); IP_STATS_INC(ip.drop); - snmp_inc_ipindiscards(); + snmp_inc_ipinhdrerrors(); return ERR_OK; } #endif /* 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))); + pbuf_realloc(p, iphdr_len); /* match packet against an interface, i.e. is this packet for us? */ - for (netif = netif_list; netif != NULL; netif = netif->next) { - - LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\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 LWIP_IGMP + if (ip_addr_ismulticast(&(iphdr->dest))) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &(iphdr->dest)))) { + netif = inp; + } else { + netif = NULL; + } + } else +#endif /* LWIP_IGMP */ + { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\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))); - /* interface is up and configured? */ - if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) - { - /* unicast to this interface address? */ - if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || - /* or broadcast on this interface network address? */ - ip_addr_isbroadcast(&(iphdr->dest), netif)) { - LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", - netif->name[0], netif->name[1])); - /* break out of for loop */ - break; + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { + /* unicast to this interface address? */ + if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(&(iphdr->dest), netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } } - } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); } + #if LWIP_DHCP /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. @@ -255,28 +312,48 @@ ip_input(struct pbuf *p, struct netif *i if (netif == NULL) { /* remote port is DHCP server? */ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { - LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", - ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest))); - if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest) == DHCP_CLIENT_PORT) { - LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n")); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest))); + if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest) == DHCP_CLIENT_PORT) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n")); netif = inp; + check_ip_src = 0; } } } #endif /* LWIP_DHCP */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if LWIP_DHCP + /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ + if (check_ip_src && (iphdr->src.addr != 0)) +#endif /* LWIP_DHCP */ + { if ((ip_addr_isbroadcast(&(iphdr->src), inp)) || + (ip_addr_ismulticast(&(iphdr->src)))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + return ERR_OK; + } + } + /* packet not for us? */ if (netif == NULL) { /* packet not for us, route or discard */ - LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: packet not for us.\n")); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); #if IP_FORWARD /* non-broadcast packet? */ if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) { /* try to forward IP packet on (other) interfaces */ ip_forward(p, iphdr, inp); - } - else + } else #endif /* IP_FORWARD */ { + snmp_inc_ipinaddrerrors(); snmp_inc_ipindiscards(); } pbuf_free(p); @@ -296,72 +373,97 @@ ip_input(struct pbuf *p, struct netif *i iphdr = p->payload; #else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ pbuf_free(p); - LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", ntohs(IPH_OFFSET(iphdr)))); IP_STATS_INC(ip.opterr); IP_STATS_INC(ip.drop); - snmp_inc_ipunknownprotos(); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); return ERR_OK; #endif /* IP_REASSEMBLY */ } -#if IP_OPTIONS == 0 /* no support for IP options in the IP header? */ - if (iphdrlen > IP_HLEN) { - LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS == 0).\n")); +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if((iphdr_hlen > IP_HLEN && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); pbuf_free(p); IP_STATS_INC(ip.opterr); IP_STATS_INC(ip.drop); - snmp_inc_ipunknownprotos(); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); return ERR_OK; } -#endif /* IP_OPTIONS == 0 */ +#endif /* IP_OPTIONS_ALLOWED == 0 */ /* send to upper layers */ LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); ip_debug_print(p); LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + current_netif = inp; + current_header = iphdr; + #if LWIP_RAW /* raw input did not eat the packet? */ - if (raw_input(p, inp) == 0) { + if (raw_input(p, inp) == 0) #endif /* LWIP_RAW */ + { - switch (IPH_PROTO(iphdr)) { + switch (IPH_PROTO(iphdr)) { #if LWIP_UDP - case IP_PROTO_UDP: - case IP_PROTO_UDPLITE: - snmp_inc_ipindelivers(); - udp_input(p, inp); - break; + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; #endif /* LWIP_UDP */ #if LWIP_TCP - case IP_PROTO_TCP: - snmp_inc_ipindelivers(); - tcp_input(p, inp); - break; + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; #endif /* LWIP_TCP */ - case IP_PROTO_ICMP: - snmp_inc_ipindelivers(); - icmp_input(p, inp); - break; - default: - /* send ICMP destination protocol unreachable unless is was a broadcast */ - if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && - !ip_addr_ismulticast(&(iphdr->dest))) { - p->payload = iphdr; - icmp_dest_unreach(p, ICMP_DUR_PROTO); +#if LWIP_ICMP + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p,inp,&(iphdr->dest)); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && + !ip_addr_ismulticast(&(iphdr->dest))) { + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinunknownprotos(); } - pbuf_free(p); - - LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + } - IP_STATS_INC(ip.proterr); - IP_STATS_INC(ip.drop); - snmp_inc_ipunknownprotos(); - } -#if LWIP_RAW - } /* LWIP_RAW */ -#endif + current_netif = NULL; + current_header = NULL; + return ERR_OK; } @@ -370,21 +472,77 @@ ip_input(struct pbuf *p, struct netif *i * 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. + * If the destination IP address is IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" */ - err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t tos, u8_t proto, struct netif *netif) { +#if IP_OPTIONS_SEND + return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if() but with the possibility to include IP options: + * + * @ param ip_options pointer to the IP options, copied into the IP header + * @ param optlen length of ip_options + */ +err_t ip_output_if_opt(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ struct ip_hdr *iphdr; - u16_t ip_id = 0; + static u16_t ip_id = 0; snmp_inc_ipoutrequests(); + /* Should the IP header be generated or is it already included in p? */ if (dest != IP_HDRINCL) { + u16_t ip_hlen = IP_HLEN; +#if IP_OPTIONS_SEND + u16_t optlen_aligned = 0; + if (optlen != 0) { + /* round up to a multiple of 4 */ + optlen_aligned = ((optlen + 3) & ~3); + ip_hlen += optlen_aligned; + /* First write in the IP options */ + if (pbuf_header(p, optlen_aligned)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n")); + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + MEMCPY(p->payload, ip_options, optlen); + if (optlen < optlen_aligned) { + /* zero the remaining bytes */ + memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + } + } +#endif /* IP_OPTIONS_SEND */ + /* generate IP header */ if (pbuf_header(p, IP_HLEN)) { - LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n")); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n")); IP_STATS_INC(ip.err); snmp_inc_ipoutdiscards(); @@ -392,15 +550,17 @@ ip_output_if(struct pbuf *p, struct ip_a } iphdr = p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); IPH_TTL_SET(iphdr, ttl); IPH_PROTO_SET(iphdr, proto); ip_addr_set(&(iphdr->dest), dest); - IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos); + IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos); IPH_LEN_SET(iphdr, htons(p->tot_len)); - IPH_OFFSET_SET(iphdr, htons(IP_DF)); + IPH_OFFSET_SET(iphdr, 0); IPH_ID_SET(iphdr, htons(ip_id)); ++ip_id; @@ -412,34 +572,54 @@ ip_output_if(struct pbuf *p, struct ip_a IPH_CHKSUM_SET(iphdr, 0); #if CHECKSUM_GEN_IP - IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); #endif } else { + /* IP header already included in p */ iphdr = p->payload; dest = &(iphdr->dest); } -#if IP_FRAG - /* don't fragment if interface has mtu set to 0 [loopif] */ - if (netif->mtu && (p->tot_len > netif->mtu)) - return ip_frag(p,netif,dest); -#endif - IP_STATS_INC(ip.xmit); LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); ip_debug_print(p); +#if ENABLE_LOOPBACK + if (ip_addr_cmp(dest, &netif->ip_addr)) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + return netif_loop_output(netif, p, dest); + } +#endif /* ENABLE_LOOPBACK */ +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + return ip_frag(p,netif,dest); + } +#endif + LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); - return netif->output(netif, p, dest); } /** * Simple interface to ip_output_if. It finds the outgoing network * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values */ - err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t tos, u8_t proto) @@ -447,17 +627,58 @@ ip_output(struct pbuf *p, struct ip_addr struct netif *netif; if ((netif = ip_route(dest)) == NULL) { - LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); - + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); IP_STATS_INC(ip.rterr); - snmp_inc_ipoutdiscards(); return ERR_RTE; } return ip_output_if(p, src, dest, ttl, tos, proto, netif); } +#if LWIP_NETIF_HWADDRHINT +/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + netif->addr_hint = addr_hint; + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + netif->addr_hint = NULL; + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + #if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ void ip_debug_print(struct pbuf *p) { @@ -500,9 +721,3 @@ ip_debug_print(struct pbuf *p) LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); } #endif /* IP_DEBUG */ - - - - - - diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/ip_addr.c b/packages/net/lwip_tcpip/current/src/core/ipv4/ip_addr.c --- a/packages/net/lwip_tcpip/current/src/core/ipv4/ip_addr.c +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/ip_addr.c @@ -1,3 +1,9 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -30,27 +36,33 @@ * */ +#include "lwip/opt.h" #include "lwip/ip_addr.h" #include "lwip/inet.h" #include "lwip/netif.h" +#define IP_ADDR_ANY_VALUE 0x00000000UL +#define IP_ADDR_BROADCAST_VALUE 0xffffffffUL + /* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ -const struct ip_addr ip_addr_any = { 0x00000000UL }; -const struct ip_addr ip_addr_broadcast = { 0xffffffffUL }; +const struct ip_addr ip_addr_any = { IP_ADDR_ANY_VALUE }; +const struct ip_addr ip_addr_broadcast = { IP_ADDR_BROADCAST_VALUE }; -/* Determine if an address is a broadcast address on a network interface +/** + * Determine if an address is a broadcast address on a network interface * * @param addr address to be checked * @param netif the network interface against which the address is checked * @return returns non-zero if the address is a broadcast address - * */ - u8_t ip_addr_isbroadcast(struct ip_addr *addr, struct netif *netif) { + u32_t addr2test; + + addr2test = addr->addr; /* all ones (broadcast) or all zeroes (old skool broadcast) */ - if ((addr->addr == ip_addr_broadcast.addr) || - (addr->addr == ip_addr_any.addr)) + if ((~addr2test == IP_ADDR_ANY_VALUE) || + (addr2test == IP_ADDR_ANY_VALUE)) return 1; /* no broadcast support on this network interface? */ else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) @@ -58,13 +70,13 @@ u8_t ip_addr_isbroadcast(struct ip_addr * nor can we check against any broadcast addresses */ return 0; /* address matches network interface address exactly? => no broadcast */ - else if (addr->addr == netif->ip_addr.addr) + else if (addr2test == netif->ip_addr.addr) return 0; /* on the same (sub) network... */ else if (ip_addr_netcmp(addr, &(netif->ip_addr), &(netif->netmask)) /* ...and host identifier bits are all ones? =>... */ - && ((addr->addr & ~netif->netmask.addr) == - (ip_addr_broadcast.addr & ~netif->netmask.addr))) + && ((addr2test & ~netif->netmask.addr) == + (IP_ADDR_BROADCAST_VALUE & ~netif->netmask.addr))) /* => network broadcast address */ return 1; else diff --git a/packages/net/lwip_tcpip/current/src/core/ipv4/ip_frag.c b/packages/net/lwip_tcpip/current/src/core/ipv4/ip_frag.c --- a/packages/net/lwip_tcpip/current/src/core/ipv4/ip_frag.c +++ b/packages/net/lwip_tcpip/current/src/core/ipv4/ip_frag.c @@ -1,6 +1,6 @@ -/* @file - * - * This is the IP packet segmentation and reassembly implementation. +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. * */ @@ -33,73 +33,430 @@ * This file is part of the lwIP TCP/IP stack. * * Author: Jani Monoses + * Simon Goldschmidt * original reassembly code by Adam Dunkels * */ +#include "lwip/opt.h" +#include "lwip/ip_frag.h" +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + #include -#include "lwip/opt.h" -/* #include "lwip/sys.h" */ -#include "lwip/ip.h" -#include "lwip/ip_frag.h" -#include "lwip/netif.h" -#include "lwip/stats.h" - - -/* - * Copy len bytes from offset in pbuf to buffer +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! * - * helper used by both ip_reass and ip_frag + * @todo: work with IP header options */ -static struct pbuf * -copy_from_pbuf(struct pbuf *p, u16_t * offset, - u8_t * buffer, u16_t len) -{ - u16_t l; - p->payload = (u8_t *)p->payload + *offset; - p->len -= *offset; - while (len) { - l = len < p->len ? len : p->len; - memcpy(buffer, p->payload, l); - buffer += l; - len -= l; - if (len) - p = p->next; - else - *offset = l; - } - return p; -} +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ -#define IP_REASS_BUFSIZE 5760 -#define IP_REASS_MAXAGE 30 -#define IP_REASS_TMO 1000 +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ -static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE]; -static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1]; -static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f, - 0x0f, 0x07, 0x03, 0x01 -}; -static u16_t ip_reasslen; -static u8_t ip_reassflags; #define IP_REASS_FLAG_LASTFRAG 0x01 -static u8_t ip_reasstmr; +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has to be packed since it has to fit inside the IP header. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); /** * Reassembly timer base function * for both NO_SYS == 0 and 1 (!). * - * Should be called every 1000 msec. + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). */ void ip_reass_tmr(void) { - if (ip_reasstmr > 0) { - ip_reasstmr--; + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + int pbufs_freed = 0; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + snmp_inc_ipreasmfails(); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + pbufs_freed += pbuf_clen(p); + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + pbufs_freed += pbuf_clen(pcur); + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reass struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no wholes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no wholes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { + valid = 0; + } else { + /* and check that there are no wholes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ } /** @@ -111,208 +468,206 @@ ip_reass_tmr(void) struct pbuf * ip_reass(struct pbuf *p) { - struct pbuf *q; - struct ip_hdr *fraghdr, *iphdr; + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; u16_t offset, len; - u16_t i; + u8_t clen; + struct ip_reassdata *ipr_prev = NULL; IPFRAG_STATS_INC(ip_frag.recv); + snmp_inc_ipreasmreqds(); - 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) { - LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n")); - memcpy(iphdr, fraghdr, IP_HLEN); - ip_reasstmr = IP_REASS_MAXAGE; - ip_reassflags = 0; - /* Clear the bitmap. */ - memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap)); + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; } - /* 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)) { - LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", - ntohs(IPH_ID(fraghdr)))); - IPFRAG_STATS_INC(ip_frag.cachehit); - /* 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) { - LWIP_DEBUGF(IP_REASS_DEBUG, - ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset, - offset + len, IP_REASS_BUFSIZE)); - ip_reasstmr = 0; - goto nullreturn; - } - - /* Copy the fragment into the reassembly buffer, at the right - offset. */ - LWIP_DEBUGF(IP_REASS_DEBUG, - ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset, - IP_HLEN + offset, IP_HLEN + offset + len)); - i = IPH_HL(fraghdr) * 4; - copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len); - - /* Update the bitmap. */ - if (offset / (8 * 8) == (offset + len) / (8 * 8)) { - LWIP_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. */ - LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)", - offset / (8 * 8) < sizeof(ip_reassbitmap)); - 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. */ - LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)", - offset / (8 * 8) < sizeof(ip_reassbitmap)); - ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7]; - LWIP_DEBUGF(IP_REASS_DEBUG, - ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\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; - } - LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)", - (offset + len) / (8 * 8) < sizeof(ip_reassbitmap)); - 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. */ + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; - if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) { - ip_reassflags |= IP_REASS_FLAG_LASTFRAG; - ip_reasslen = offset + len; - LWIP_DEBUGF(IP_REASS_DEBUG, - ("ip_reass: last fragment seen, total len %"S16_F"\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. */ - LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)", - ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)); - for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) { - if (ip_reassbitmap[i] != 0xff) { - LWIP_DEBUGF(IP_REASS_DEBUG, - ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\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. */ - LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)", - ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)); - if (ip_reassbitmap[ip_reasslen / (8 * 8)] != - (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) { - LWIP_DEBUGF(IP_REASS_DEBUG, - ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\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. */ - ip_reasslen += IP_HLEN; - - IPH_LEN_SET(iphdr, htons(ip_reasslen)); - 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 - available data in the pbuf is given by the q->len variable. */ - LWIP_DEBUGF(IP_REASS_DEBUG, - ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n", - (void *)&ip_reassbuf[i], i, q->payload, - q->len > ip_reasslen - i ? ip_reasslen - i : q->len)); - memcpy(q->payload, &ip_reassbuf[i], - q->len > ip_reasslen - i ? ip_reasslen - i : q->len); - i += q->len; - } - IPFRAG_STATS_INC(ip_frag.fw); - } else { - IPFRAG_STATS_INC(ip_frag.memerr); - } - LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p)); - return p; + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; } } + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if(ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set calculate the correct checksum? */ + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while(r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); IPFRAG_STATS_INC(ip_frag.drop); pbuf_free(p); return NULL; } +#endif /* IP_REASSEMBLY */ -#define MAX_MTU 1500 -static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)]; +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; +#endif /* IP_FRAG_USES_STATIC_BUF */ /** * Fragment an IP datagram if too large for the netif. * * Chop the datagram in MTU sized chunks and send them in order - * by using a fixed size static memory buffer (PBUF_ROM) + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise */ err_t ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest) { struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF struct pbuf *header; +#else + struct pbuf *newpbuf; + struct ip_hdr *original_iphdr; +#endif struct ip_hdr *iphdr; - u16_t nfb = 0; + u16_t nfb; u16_t left, cop; u16_t mtu = netif->mtu; u16_t ofo, omf; u16_t last; u16_t poff = IP_HLEN; u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); return ERR_MEM; } rambuf->tot_len = rambuf->len = mtu; - rambuf->payload = MEM_ALIGN((void *)buf); + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); /* Copy the IP header in it */ iphdr = rambuf->payload; - memcpy(iphdr, p->payload, IP_HLEN); + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ /* Save original offset */ tmp = ntohs(IPH_OFFSET(iphdr)); @@ -321,29 +676,77 @@ ip_frag(struct pbuf *p, struct netif *ne left = p->tot_len - IP_HLEN; + nfb = (mtu - IP_HLEN) / 8; + while (left) { last = (left <= mtu - IP_HLEN); /* Set new offset and MF flag */ - ofo += nfb; tmp = omf | (IP_OFFMASK & (ofo)); if (!last) tmp = tmp | IP_MF; - IPH_OFFSET_SET(iphdr, htons(tmp)); /* Fill this fragment */ - nfb = (mtu - IP_HLEN) / 8; cop = last ? left : nfb * 8; - p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop); +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); + if (newpbuf == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf->payload = p->payload; + newpbuf->len = newpbuf->tot_len = newpbuflen; + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) + p = p->next; + } + poff = newpbuflen; +#endif /* IP_FRAG_USES_STATIC_BUF */ /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); IPH_CHKSUM_SET(iphdr, 0); IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#if IP_FRAG_USES_STATIC_BUF if (last) pbuf_realloc(rambuf, left + IP_HLEN); + /* This part is ugly: we alloc a RAM based pbuf for * the link level header for each chunk and then * free it.A PBUF_ROM style pbuf for which pbuf_header @@ -354,13 +757,36 @@ ip_frag(struct pbuf *p, struct netif *ne pbuf_chain(header, rambuf); netif->output(netif, header, dest); IPFRAG_STATS_INC(ip_frag.xmit); + snmp_inc_ipfragcreates(); pbuf_free(header); } else { - pbuf_free(rambuf); - return ERR_MEM; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + return ERR_MEM; } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ left -= cop; + ofo += nfb; } +#if IP_FRAG_USES_STATIC_BUF pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + snmp_inc_ipfragoks(); return ERR_OK; } +#endif /* IP_FRAG */ diff --git a/packages/net/lwip_tcpip/current/src/core/mem.c b/packages/net/lwip_tcpip/current/src/core/mem.c --- a/packages/net/lwip_tcpip/current/src/core/mem.c +++ b/packages/net/lwip_tcpip/current/src/core/mem.c @@ -1,14 +1,30 @@ -/** @file - * +/** + * @file * Dynamic memory manager * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END */ -/* +/* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, + * 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, @@ -17,67 +33,193 @@ * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. + * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * Simon Goldschmidt * */ +#include "lwip/opt.h" + +#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/stats.h" + #include -#include "lwip/arch.h" -#include "lwip/opt.h" -#include "lwip/def.h" -#include "lwip/mem.h" +#if MEM_USE_POOLS +/* lwIP head implemented with different sized pools */ + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + sizeof(struct memp_malloc_helper); + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr++) { +#if MEM_USE_POOLS_TRY_BIGGER_POOL +again: +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + /* is this pool big enough to hold an element of the required size + plus a struct memp_malloc_helper that saves the pool this element came from? */ + if (required_size <= memp_sizes[poolnr]) { + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + return NULL; + } + element = (struct memp_malloc_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already + taken care of in memp.c */ +#if MEM_USE_POOLS_TRY_BIGGER_POOL + /** Try a bigger pool if this one is empty! */ + if (poolnr < MEMP_POOL_LAST) { + poolnr++; + goto again; + } +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + return NULL; + } -#include "lwip/sys.h" + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct memp_malloc_helper */ + element++; + + return element; +} -#include "lwip/stats.h" +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + /* get the original struct memp_malloc_helper */ + hmem--; + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. + */ struct mem { - mem_size_t next, prev; -#if MEM_ALIGNMENT == 1 + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[next]) of the next struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ u8_t used; -#elif MEM_ALIGNMENT == 2 - u16_t used; -#elif MEM_ALIGNMENT == 4 - u32_t used; -#elif MEM_ALIGNMENT == 8 - u64_t used; -#else -#error "unhandled MEM_ALIGNMENT size" -#endif /* MEM_ALIGNMENT */ -}; +}; +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** the heap. we need one struct mem at the end and some room for alignment */ +static u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ static struct mem *ram_end; -static u8_t ram[MEM_SIZE + sizeof(struct mem) + MEM_ALIGNMENT]; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; -#define MIN_SIZE 12 -#if 0 /* this one does not align correctly for some, resulting in crashes */ -#define SIZEOF_STRUCT_MEM (unsigned int)MEM_ALIGN_SIZE(sizeof(struct mem)) -#else -#define SIZEOF_STRUCT_MEM (sizeof(struct mem) + \ - (((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \ - (4 - (sizeof(struct mem) % MEM_ALIGNMENT)))) -#endif - -static struct mem *lfree; /* pointer to the lowest free block */ - +/** concurrent access protection */ static sys_sem_t mem_sem; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_arch_sem_wait(mem_sem, 0) +#define LWIP_MEM_FREE_UNPROTECT() sys_sem_signal(mem_sem) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_realloc() + * + * This assumes access to the heap is protected by the calling function + * already. + */ static void plug_holes(struct mem *mem) { @@ -87,12 +229,13 @@ plug_holes(struct mem *mem) LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); - + /* plug hole forward */ - LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE", mem->next <= MEM_SIZE); - + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + nmem = (struct mem *)&ram[mem->next]; if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ if (lfree == nmem) { lfree = mem; } @@ -103,146 +246,247 @@ plug_holes(struct mem *mem) /* plug hole backward */ pmem = (struct mem *)&ram[mem->prev]; if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ if (lfree == mem) { lfree = pmem; } pmem->next = mem->next; ((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram; } +} -} +/** + * Zero the heap and initialize start, end and lowest-free + */ void mem_init(void) { struct mem *mem; - memset(ram, 0, MEM_SIZE); + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = LWIP_MEM_ALIGN(ram_heap); + /* initialize the start of the heap */ mem = (struct mem *)ram; - mem->next = MEM_SIZE; + mem->next = MEM_SIZE_ALIGNED; mem->prev = 0; mem->used = 0; - ram_end = (struct mem *)&ram[MEM_SIZE]; + /* initialize the end of the heap */ + ram_end = (struct mem *)&ram[MEM_SIZE_ALIGNED]; ram_end->used = 1; - ram_end->next = MEM_SIZE; - ram_end->prev = MEM_SIZE; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; mem_sem = sys_sem_new(1); + /* initialize the lowest-free pointer to the start of the heap */ lfree = (struct mem *)ram; -#if MEM_STATS - lwip_stats.mem.avail = MEM_SIZE; -#endif /* MEM_STATS */ + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); } + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ void mem_free(void *rmem) { struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); if (rmem == NULL) { - LWIP_DEBUGF(MEM_DEBUG | DBG_TRACE | 2, ("mem_free(p == NULL) was called.\n")); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n")); return; } - - sys_sem_wait(mem_sem); + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && (u8_t *)rmem < (u8_t *)ram_end); - + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { - LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_free: illegal memory\n")); -#if MEM_STATS - ++lwip_stats.mem.err; -#endif /* MEM_STATS */ - sys_sem_signal(mem_sem); + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); return; } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); - + /* ... which has to be in a used state ... */ LWIP_ASSERT("mem_free: mem->used", mem->used); - + /* ... and is now unused. */ mem->used = 0; if (mem < lfree) { + /* the newly freed struct is now the lowest */ lfree = mem; } - -#if MEM_STATS - lwip_stats.mem.used -= mem->next - ((u8_t *)mem - ram); - -#endif /* MEM_STATS */ + + MEM_STATS_DEC_USED(used, mem->next - ((u8_t *)mem - ram)); + + /* finally, see if prev or next are free also */ 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); - } - memcpy(nmem, rmem, newsize); - mem_free(rmem); - return nmem; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); } +/** + * In contrast to its name, mem_realloc can only shrink memory, not expand it. + * Since the only use (for now) is in pbuf_realloc (which also can only shrink), + * this shouldn't be a problem! + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ void * mem_realloc(void *rmem, mem_size_t newsize) { mem_size_t size; mem_size_t ptr, ptr2; struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); /* Expand the size of the allocated memory region so that we can adjust for alignment. */ - if ((newsize % MEM_ALIGNMENT) != 0) { - newsize += MEM_ALIGNMENT - ((newsize + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT); + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if(newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; } - - if (newsize > MEM_SIZE) { + + if (newsize > MEM_SIZE_ALIGNED) { return NULL; } - - sys_sem_wait(mem_sem); - + LWIP_ASSERT("mem_realloc: legal memory", (u8_t *)rmem >= (u8_t *)ram && (u8_t *)rmem < (u8_t *)ram_end); - + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { - LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_realloc: illegal memory\n")); + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_realloc: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); return rmem; } + /* Get the corresponding struct mem ... */ mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); - + /* ... and its offset pointer */ ptr = (u8_t *)mem - ram; size = mem->next - ptr - SIZEOF_STRUCT_MEM; -#if MEM_STATS - lwip_stats.mem.used -= (size - newsize); -#endif /* MEM_STATS */ - - if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) { + LWIP_ASSERT("mem_realloc can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + MEM_STATS_DEC_USED(used, (size - newsize)); + + mem2 = (struct mem *)&ram[mem->next]; + if(mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)&ram[ptr2]; + } + mem2 = (struct mem *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; mem2 = (struct mem *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; mem->next = ptr2; - if (mem2->next != MEM_SIZE) { + if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)&ram[mem2->next])->prev = ptr2; } - - plug_holes(mem2); + /* the original mem->next is used, so no need to plug holes! */ } - sys_sem_signal(mem_sem); + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); return rmem; } + +/** + * Adam's mem_malloc() plus solution for bug #17922 + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ void * mem_malloc(mem_size_t size) { mem_size_t ptr, ptr2; struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); if (size == 0) { return NULL; @@ -250,61 +494,140 @@ mem_malloc(mem_size_t size) /* 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); + size = LWIP_MEM_ALIGN_SIZE(size); + + if(size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; } - - if (size > MEM_SIZE) { + + if (size > MEM_SIZE_ALIGNED) { return NULL; } - - sys_sem_wait(mem_sem); + + /* protect the heap from concurrent access */ + sys_arch_sem_wait(mem_sem, 0); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ - for (ptr = (u8_t *)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; + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)&ram[ptr])->next) { + mem = (struct mem *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + local_mem_free_count = mem_free_count; } - - mem2->used = 0; - mem->used = 1; -#if MEM_STATS - lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM); - /* if (lwip_stats.mem.max < lwip_stats.mem.used) { - lwip_stats.mem.max = lwip_stats.mem.used; - } */ - if (lwip_stats.mem.max < ptr2) { - lwip_stats.mem.max = ptr2; - } -#endif /* MEM_STATS */ + mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; - if (mem == lfree) { - /* Find next free block after mem */ - while (lfree->used && lfree != ram_end) { - lfree = (struct mem *)&ram[lfree->next]; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - ((u8_t *)mem - ram)); } - LWIP_ASSERT("mem_malloc: !lfree->used", !lfree->used); + + if (mem == lfree) { + /* Find next free block after mem and update lowest free pointer */ + while (lfree->used && lfree != ram_end) { + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + lfree = (struct mem *)&ram[lfree->next]; + } + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_sem_signal(mem_sem); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; } - sys_sem_signal(mem_sem); - LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", - (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); - LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", - (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); - return (u8_t *)mem + SIZEOF_STRUCT_MEM; - } - } - LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); -#if MEM_STATS - ++lwip_stats.mem.err; -#endif /* MEM_STATS */ + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while(local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); sys_sem_signal(mem_sem); return NULL; } + +#endif /* MEM_USE_POOLS */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + memset(p, 0, count * size); + } + return p; +} + +#endif /* !MEM_LIBC_MALLOC */ diff --git a/packages/net/lwip_tcpip/current/src/core/memp.c b/packages/net/lwip_tcpip/current/src/core/memp.c --- a/packages/net/lwip_tcpip/current/src/core/memp.c +++ b/packages/net/lwip_tcpip/current/src/core/memp.c @@ -1,3 +1,11 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -33,242 +41,346 @@ #include "lwip/opt.h" #include "lwip/memp.h" - #include "lwip/pbuf.h" #include "lwip/udp.h" #include "lwip/raw.h" #include "lwip/tcp.h" +#include "lwip/igmp.h" #include "lwip/api.h" #include "lwip/api_msg.h" #include "lwip/tcpip.h" - #include "lwip/sys.h" #include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.h" + +#include + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ struct memp { struct memp *next; -}; - - - -static struct memp *memp_tab[MEMP_MAX]; - -static const u16_t memp_sizes[MEMP_MAX] = { - sizeof(struct pbuf), - sizeof(struct raw_pcb), - 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 u16_t memp_num[MEMP_MAX] = { - MEMP_NUM_PBUF, - MEMP_NUM_RAW_PCB, - 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 +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ }; -static u8_t memp_memory[(MEMP_NUM_PBUF * - MEM_ALIGN_SIZE(sizeof(struct pbuf) + - sizeof(struct memp)) + - MEMP_NUM_RAW_PCB * - MEM_ALIGN_SIZE(sizeof(struct raw_pcb) + - 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_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ +/** This array holds the first free element of each pool. + * Elements form a linked list. */ +static struct memp *memp_tab[MEMP_MAX]; -#if !SYS_LIGHTWEIGHT_PROT -static sys_sem_t mutex; +#else /* MEMP_MEM_MALLOC */ + +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_MEM_MALLOC */ + +/** This array holds the element sizes of each pool. */ +#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC +static #endif +const u16_t memp_sizes[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +/** This array holds the number of elements in each pool. */ +static const u16_t memp_num[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (num), +#include "lwip/memp_std.h" +}; + +/** This array holds a textual description of each pool. */ +#ifdef LWIP_DEBUG +static const char *memp_desc[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (desc), +#include "lwip/memp_std.h" +}; +#endif /* LWIP_DEBUG */ + +/** This is the actual memory used by the pools. */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; #if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle + */ static int memp_sanity(void) { s16_t i, c; struct memp *m, *n; - for(i = 0; i < MEMP_MAX; i++) { - for(m = memp_tab[i]; m != NULL; m = m->next) { + 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; + for (n = memp_tab[i]; n != NULL; n = n->next) { + if (n == m && --c < 0) { + return 0; } - if (c < 0) return 0; /* LW was: abort(); */ } } } return 1; } #endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param memp_size the element size of the pool p comes from + */ +static void +memp_overflow_check_element(struct memp *p, u16_t memp_size) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + LWIP_ASSERT("detected memp underflow!", 0); + } + } +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_size; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + LWIP_ASSERT("detected memp overflow!", 0); + } + } +#endif +} +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + + p = LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element(p, memp_sizes[i]); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} + +/** + * Initialize the restricted areas of all memp elements in every pool. + */ +static void +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + + p = LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[i]; + memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize this module. + * + * Carves out memp_memory into linked lists for each pool-type. + */ void memp_init(void) { - struct memp *m, *memp; + struct memp *memp; u16_t i, j; - u16_t size; - -#if MEMP_STATS - for(i = 0; i < MEMP_MAX; ++i) { - lwip_stats.memp[i].used = lwip_stats.memp[i].max = - lwip_stats.memp[i].err = 0; - lwip_stats.memp[i].avail = memp_num[i]; + + for (i = 0; i < MEMP_MAX; ++i) { + MEMP_STATS_AVAIL(used, i, 0); + MEMP_STATS_AVAIL(max, i, 0); + MEMP_STATS_AVAIL(err, i, 0); + MEMP_STATS_AVAIL(avail, i, 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 = LWIP_MEM_ALIGN(memp_memory); + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = memp_tab[i]; memp_tab[i] = memp; - m = memp; - - for(j = 0; j < memp_num[i]; ++j) { - m->next = (struct memp *)MEM_ALIGN((u8_t *)m + size); - memp = m; - m = m->next; - } - memp->next = NULL; - memp = m; - } else { - memp_tab[i] = NULL; + memp = (struct memp *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i] +#if MEMP_OVERFLOW_CHECK + + MEMP_SANITY_REGION_AFTER_ALIGNED +#endif + ); } } - -#if !SYS_LIGHTWEIGHT_PROT - mutex = sys_sem_new(1); -#endif - - +#if MEMP_OVERFLOW_CHECK + memp_overflow_init(); + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK */ } +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * the debug version has two more parameters: + * @param file file name calling this function + * @param line number of line where this function is called + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ void * +#if !MEMP_OVERFLOW_CHECK memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif { struct memp *memp; - void *mem; -#if SYS_LIGHTWEIGHT_PROT SYS_ARCH_DECL_PROTECT(old_level); -#endif - LWIP_ASSERT("memp_malloc: type < MEMP_MAX", type < MEMP_MAX); + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); -#if SYS_LIGHTWEIGHT_PROT SYS_ARCH_PROTECT(old_level); -#else /* SYS_LIGHTWEIGHT_PROT */ - sys_sem_wait(mutex); -#endif /* SYS_LIGHTWEIGHT_PROT */ +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ memp = memp_tab[type]; - if (memp != NULL) { - memp_tab[type] = memp->next; + if (memp != NULL) { + memp_tab[type] = memp->next; +#if MEMP_OVERFLOW_CHECK memp->next = NULL; -#if MEMP_STATS - ++lwip_stats.memp[type].used; - if (lwip_stats.memp[type].used > lwip_stats.memp[type].max) { - lwip_stats.memp[type].max = lwip_stats.memp[type].used; - } -#endif /* MEMP_STATS */ -#if SYS_LIGHTWEIGHT_PROT - SYS_ARCH_UNPROTECT(old_level); -#else /* SYS_LIGHTWEIGHT_PROT */ - sys_sem_signal(mutex); -#endif /* SYS_LIGHTWEIGHT_PROT */ + memp->file = file; + memp->line = line; +#endif /* MEMP_OVERFLOW_CHECK */ + MEMP_STATS_INC_USED(used, type); LWIP_ASSERT("memp_malloc: memp properly aligned", - ((mem_ptr_t)MEM_ALIGN((u8_t *)memp + sizeof(struct memp)) % MEM_ALIGNMENT) == 0); - - mem = MEM_ALIGN((u8_t *)memp + sizeof(struct memp)); - return mem; + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)((u8_t*)memp + MEMP_SIZE); } else { - LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: out of memory in pool %"S16_F"\n", type)); -#if MEMP_STATS - ++lwip_stats.memp[type].err; -#endif /* MEMP_STATS */ -#if SYS_LIGHTWEIGHT_PROT + LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + MEMP_STATS_INC(err, type); + } + SYS_ARCH_UNPROTECT(old_level); -#else /* SYS_LIGHTWEIGHT_PROT */ - sys_sem_signal(mutex); -#endif /* SYS_LIGHTWEIGHT_PROT */ - return NULL; - } + + return memp; } +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ void memp_free(memp_t type, void *mem) { struct memp *memp; -#if SYS_LIGHTWEIGHT_PROT SYS_ARCH_DECL_PROTECT(old_level); -#endif /* SYS_LIGHTWEIGHT_PROT */ if (mem == NULL) { return; } - memp = (struct memp *)((u8_t *)mem - sizeof(struct memp)); + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + memp = (struct memp *)((u8_t*)mem - MEMP_SIZE); -#if SYS_LIGHTWEIGHT_PROT - SYS_ARCH_PROTECT(old_level); -#else /* SYS_LIGHTWEIGHT_PROT */ - sys_sem_wait(mutex); -#endif /* SYS_LIGHTWEIGHT_PROT */ + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#else + memp_overflow_check_element(memp, memp_sizes[type]); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ -#if MEMP_STATS - lwip_stats.memp[type].used--; -#endif /* MEMP_STATS */ + MEMP_STATS_DEC(used, type); memp->next = memp_tab[type]; memp_tab[type] = memp; #if MEMP_SANITY_CHECK LWIP_ASSERT("memp sanity", memp_sanity()); -#endif +#endif /* MEMP_SANITY_CHECK */ -#if SYS_LIGHTWEIGHT_PROT SYS_ARCH_UNPROTECT(old_level); -#else /* SYS_LIGHTWEIGHT_PROT */ - sys_sem_signal(mutex); -#endif /* SYS_LIGHTWEIGHT_PROT */ } +#endif /* MEMP_MEM_MALLOC */ diff --git a/packages/net/lwip_tcpip/current/src/core/netif.c b/packages/net/lwip_tcpip/current/src/core/netif.c --- a/packages/net/lwip_tcpip/current/src/core/netif.c +++ b/packages/net/lwip_tcpip/current/src/core/netif.c @@ -1,7 +1,7 @@ /** * @file + * lwIP network interface abstraction * - * lwIP network interface abstraction */ /* @@ -42,9 +42,37 @@ #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/tcp.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.h" +#if ENABLE_LOOPBACK +#include "lwip/sys.h" +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ -struct netif *netif_list = NULL; -struct netif *netif_default = NULL; +#if LWIP_AUTOIP +#include "lwip/autoip.h" +#endif /* LWIP_AUTOIP */ +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n) { if (n->status_callback) (n->status_callback)(n); } +#else +#define NETIF_STATUS_CALLBACK(n) { /* NOP */ } +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) { if (n->link_callback) (n->link_callback)(n); } +#else +#define NETIF_LINK_CALLBACK(n) { /* NOP */ } +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; /** * Add a network interface to the list of lwIP netifs. @@ -67,16 +95,45 @@ netif_add(struct netif *netif, struct ip err_t (* init)(struct netif *netif), err_t (* input)(struct pbuf *p, struct netif *netif)) { - static s16_t netifnum = 0; - + static u8_t netifnum = 0; + + /* reset new interface configuration state */ + netif->ip_addr.addr = 0; + netif->netmask.addr = 0; + netif->gw.addr = 0; + netif->flags = 0; #if LWIP_DHCP /* netif not under DHCP control by default */ netif->dhcp = NULL; -#endif +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /* netif not under AutoIP control by default */ + netif->autoip = NULL; +#endif /* LWIP_AUTOIP */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + /* remember netif specific state information data */ netif->state = state; netif->num = netifnum++; netif->input = input; +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ netif_set_addr(netif, ipaddr, netmask, gw); @@ -88,6 +145,15 @@ netif_add(struct netif *netif, struct ip /* add this netif to the list */ netif->next = netif_list; netif_list = netif; + snmp_inc_iflist(); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start( netif); + } +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", netif->name[0], netif->name[1])); ip_addr_debug_print(NETIF_DEBUG, ipaddr); @@ -99,8 +165,17 @@ netif_add(struct netif *netif, struct ip return netif; } +/** + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ void -netif_set_addr(struct netif *netif,struct ip_addr *ipaddr, struct ip_addr *netmask, +netif_set_addr(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw) { netif_set_ipaddr(netif, ipaddr); @@ -108,13 +183,28 @@ netif_set_addr(struct netif *netif,struc netif_set_gw(netif, gw); } +/** + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ void netif_remove(struct netif * netif) { if ( netif == NULL ) return; +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop( netif); + } +#endif /* LWIP_IGMP */ + + snmp_delete_ipaddridx_tree(netif); + /* is it the first netif? */ if (netif_list == netif) { netif_list = netif->next; + snmp_dec_iflist(); } else { /* look for netif further down the list */ @@ -122,8 +212,9 @@ void netif_remove(struct netif * netif) for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { if (tmpNetif->next == netif) { tmpNetif->next = netif->next; + snmp_dec_iflist(); break; - } + } } if (tmpNetif == NULL) return; /* we didn't find any netif today */ @@ -131,10 +222,16 @@ void netif_remove(struct netif * netif) /* this netif is default? */ if (netif_default == netif) /* reset default netif */ - netif_default = NULL; + netif_set_default(NULL); LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); } +/** + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ struct netif * netif_find(char *name) { @@ -159,6 +256,15 @@ netif_find(char *name) return NULL; } +/** + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ void netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr) { @@ -172,14 +278,14 @@ netif_set_ipaddr(struct netif *netif, st if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ - LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: netif address being changed\n")); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); pcb = tcp_active_pcbs; while (pcb != NULL) { /* PCB bound to current local interface address? */ if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { /* this connection must be aborted */ struct tcp_pcb *next = pcb->next; - LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); tcp_abort(pcb); pcb = next; } else { @@ -188,7 +294,8 @@ netif_set_ipaddr(struct netif *netif, st } for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { /* PCB bound to current local interface address? */ - if (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr))) { + if ((!(ip_addr_isany(&(lpcb->local_ip)))) && + (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) { /* The PCB is listening to the old ipaddr and * is set to listen to the new one instead */ ip_addr_set(&(lpcb->local_ip), ipaddr); @@ -196,16 +303,14 @@ netif_set_ipaddr(struct netif *netif, st } } #endif + snmp_delete_ipaddridx_tree(netif); + snmp_delete_iprteidx_tree(0,netif); + /* set new IP address to netif */ ip_addr_set(&(netif->ip_addr), ipaddr); -#if 0 /* only allowed for Ethernet interfaces TODO: how can we check? */ - /** For Ethernet network interfaces, we would like to send a - * "gratuitous ARP"; this is an ARP packet sent by a node in order - * to spontaneously cause other nodes to update an entry in their - * ARP cache. From RFC 3220 "IP Mobility Support for IPv4" section 4.6. - */ - etharp_query(netif, ipaddr, NULL); -#endif - LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + snmp_insert_ipaddridx_tree(netif); + snmp_insert_iprteidx_tree(0,netif); + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", netif->name[0], netif->name[1], ip4_addr1(&netif->ip_addr), ip4_addr2(&netif->ip_addr), @@ -213,11 +318,19 @@ netif_set_ipaddr(struct netif *netif, st ip4_addr4(&netif->ip_addr))); } +/** + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ void netif_set_gw(struct netif *netif, struct ip_addr *gw) { ip_addr_set(&(netif->gw), gw); - LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", netif->name[0], netif->name[1], ip4_addr1(&netif->gw), ip4_addr2(&netif->gw), @@ -225,11 +338,23 @@ netif_set_gw(struct netif *netif, struct ip4_addr4(&netif->gw))); } +/** + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ void netif_set_netmask(struct netif *netif, struct ip_addr *netmask) { + snmp_delete_iprteidx_tree(0, netif); + /* set new netmask to netif */ ip_addr_set(&(netif->netmask), netmask); - LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + snmp_insert_iprteidx_tree(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", netif->name[0], netif->name[1], ip4_addr1(&netif->netmask), ip4_addr2(&netif->netmask), @@ -237,9 +362,25 @@ netif_set_netmask(struct netif *netif, s ip4_addr4(&netif->netmask))); } +/** + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ void netif_set_default(struct netif *netif) { + if (netif == NULL) + { + /* remove default route */ + snmp_delete_iprteidx_tree(1, netif); + } + else + { + /* install default route */ + snmp_insert_iprteidx_tree(1, netif); + } netif_default = netif; LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); @@ -256,15 +397,30 @@ netif_set_default(struct netif *netif) */ void netif_set_up(struct netif *netif) { - netif->flags |= NETIF_FLAG_UP; -} + if ( !(netif->flags & NETIF_FLAG_UP )) { + netif->flags |= NETIF_FLAG_UP; + +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif /* LWIP_SNMP */ + + NETIF_LINK_CALLBACK(netif); + NETIF_STATUS_CALLBACK(netif); -/** - * Ask if an interface is up - */ -u8_t netif_is_up(struct netif *netif) -{ - return (netif->flags & NETIF_FLAG_UP)?1:0; +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ + } } /** @@ -277,12 +433,249 @@ u8_t netif_is_up(struct netif *netif) */ void netif_set_down(struct netif *netif) { - netif->flags &= ~NETIF_FLAG_UP; + if ( netif->flags & NETIF_FLAG_UP ) + { + netif->flags &= ~NETIF_FLAG_UP; +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif + + NETIF_LINK_CALLBACK(netif); + NETIF_STATUS_CALLBACK(netif); + } +} + +/** + * Ask if an interface is up + */ +u8_t netif_is_up(struct netif *netif) +{ + return (netif->flags & NETIF_FLAG_UP)?1:0; +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, void (* status_callback)(struct netif *netif )) +{ + if ( netif ) + netif->status_callback = status_callback; +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_DHCP + if (netif->dhcp) { + dhcp_network_changed(netif); + } +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP + if (netif->autoip) { + autoip_network_changed(netif); + } +#endif /* LWIP_AUTOIP */ + + if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ + } + NETIF_LINK_CALLBACK(netif); +} + +/** + * Called by a driver when its link goes down + */ +void netif_set_link_down(struct netif *netif ) +{ + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); +} + +/** + * Ask if a link is up + */ +u8_t netif_is_link_up(struct netif *netif) +{ + return (netif->flags & NETIF_FLAG_LINK_UP) ? 1 : 0; } -void -netif_init(void) +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif )) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @param ipaddr the ip address to send the packet to (not used) + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr) { - netif_list = netif_default = NULL; + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + SYS_ARCH_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ipaddr); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + r = NULL; + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + r = NULL; + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next); + + SYS_ARCH_PROTECT(lev); + if(netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback((void (*)(void *))(netif_poll), netif); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; } +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + struct pbuf *in; + SYS_ARCH_DECL_PROTECT(lev); + + do { + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + in = netif->loop_first; + if(in != NULL) { + struct pbuf *in_end = in; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = pbuf_clen(in); + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + while(in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; + } + /* 'in_end' now points to the last pbuf from 'in' */ + if(in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + } + SYS_ARCH_UNPROTECT(lev); + + if(in != NULL) { + /* loopback packets are always IP packets! */ + if(ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + /* Don't reference the packet any more! */ + in = NULL; + } + /* go on while there is a packet on the list */ + } while(netif->loop_first != NULL); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ diff --git a/packages/net/lwip_tcpip/current/src/core/pbuf.c b/packages/net/lwip_tcpip/current/src/core/pbuf.c --- a/packages/net/lwip_tcpip/current/src/core/pbuf.c +++ b/packages/net/lwip_tcpip/current/src/core/pbuf.c @@ -14,9 +14,8 @@ * This is called a "packet queue". * * So, a packet queue consists of one or more pbuf chains, each of - * which consist of one or more pbufs. Currently, queues are only - * supported in a limited section of lwIP, this is the etharp queueing - * code. Outside of this section no packet queues are supported yet. + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. * * The differences between a pbuf chain and a packet queue are very * precise but subtle. @@ -62,9 +61,8 @@ * */ -#include +#include "lwip/opt.h" -#include "lwip/opt.h" #include "lwip/stats.h" #include "lwip/def.h" #include "lwip/mem.h" @@ -72,112 +70,82 @@ #include "lwip/pbuf.h" #include "lwip/sys.h" #include "arch/perf.h" - -static u8_t pbuf_pool_memory[MEM_ALIGNMENT - 1 + PBUF_POOL_SIZE * MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE + sizeof(struct pbuf))]; - -#if !SYS_LIGHTWEIGHT_PROT -static volatile u8_t pbuf_pool_free_lock, pbuf_pool_alloc_lock; -static sys_sem_t pbuf_pool_free_sem; +#if TCP_QUEUE_OOSEQ +#include "lwip/tcp.h" #endif -static struct pbuf *pbuf_pool = NULL; +#include + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) -/** - * 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 = NULL; - u16_t i; - - pbuf_pool = (struct pbuf *)MEM_ALIGN(pbuf_pool_memory); +#if !TCP_QUEUE_OOSEQ || NO_SYS +#define PBUF_POOL_IS_EMPTY() +#else /* !TCP_QUEUE_OOSEQ || NO_SYS */ +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ -#if PBUF_STATS - lwip_stats.pbuf.avail = PBUF_POOL_SIZE; -#endif /* PBUF_STATS */ - - /* Set up ->next pointers to link the pbufs of the pool together */ - p = pbuf_pool; +#if PBUF_POOL_FREE_OOSEQ +#include "lwip/tcpip.h" +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() +static u8_t pbuf_free_ooseq_queued; +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + * + * This must be done in the correct thread context therefore this function + * can only be used with NO_SYS=0 and through tcpip_callback. + */ +static void +pbuf_free_ooseq(void* arg) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + LWIP_UNUSED_ARG(arg); - for(i = 0; i < PBUF_POOL_SIZE; ++i) { - p->next = (struct pbuf *)((u8_t *)p + PBUF_POOL_BUFSIZE + sizeof(struct pbuf)); - p->len = p->tot_len = PBUF_POOL_BUFSIZE; - p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf))); - p->flags = PBUF_FLAG_POOL; - q = p; - p = p->next; - } + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_queued = 0; + SYS_ARCH_UNPROTECT(old_level); - /* The ->next pointer of last pbuf is NULL to indicate that there - are no more pbufs in the pool */ - q->next = NULL; - -#if !SYS_LIGHTWEIGHT_PROT - pbuf_pool_alloc_lock = 0; - pbuf_pool_free_lock = 0; - pbuf_pool_free_sem = sys_sem_new(1); -#endif + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + if (NULL != pcb->ooseq) { + /** Free the ooseq pbufs of one PCB only */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return; + } + } } -/** - * @internal only called from pbuf_alloc() - */ -static struct pbuf * -pbuf_pool_alloc(void) +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void +pbuf_pool_is_empty(void) { - struct pbuf *p = NULL; - + u8_t queued; SYS_ARCH_DECL_PROTECT(old_level); - SYS_ARCH_PROTECT(old_level); -#if !SYS_LIGHTWEIGHT_PROT - /* 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) { -#if PBUF_STATS - ++lwip_stats.pbuf.alloc_locked; -#endif /* PBUF_STATS */ - return NULL; - } - pbuf_pool_alloc_lock = 1; - if (!pbuf_pool_free_lock) { -#endif /* SYS_LIGHTWEIGHT_PROT */ - p = pbuf_pool; - if (p) { - pbuf_pool = p->next; - } -#if !SYS_LIGHTWEIGHT_PROT -#if PBUF_STATS - } else { - ++lwip_stats.pbuf.alloc_locked; -#endif /* PBUF_STATS */ - } - pbuf_pool_alloc_lock = 0; -#endif /* SYS_LIGHTWEIGHT_PROT */ + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_queued; + pbuf_free_ooseq_queued = 1; + SYS_ARCH_UNPROTECT(old_level); -#if PBUF_STATS - if (p != NULL) { - ++lwip_stats.pbuf.used; - if (lwip_stats.pbuf.used > lwip_stats.pbuf.max) { - lwip_stats.pbuf.max = lwip_stats.pbuf.used; + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) { + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_queued = 0; + SYS_ARCH_UNPROTECT(old_level); } } -#endif /* PBUF_STATS */ - - SYS_ARCH_UNPROTECT(old_level); - return p; } - +#endif /* PBUF_POOL_FREE_OOSEQ */ +#endif /* !TCP_QUEUE_OOSEQ || NO_SYS */ /** * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). @@ -186,7 +154,9 @@ pbuf_pool_alloc(void) * layer at which the pbuf is allocated and the requested size * (from the size parameter). * - * @param flag this parameter decides how and where the pbuf + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf * should be allocated as follows: * * - PBUF_RAM: buffer memory for pbuf is allocated as one large @@ -209,16 +179,16 @@ pbuf_pool_alloc(void) * is the first pbuf of a pbuf chain. */ struct pbuf * -pbuf_alloc(pbuf_layer l, u16_t length, pbuf_flag flag) +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; u16_t offset; s32_t rem_len; /* remaining length */ - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F")\n", length)); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); /* determine header offset */ offset = 0; - switch (l) { + switch (layer) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset += PBUF_TRANSPORT_HLEN; @@ -238,27 +208,31 @@ pbuf_alloc(pbuf_layer l, u16_t length, p return NULL; } - switch (flag) { + switch (type) { case PBUF_POOL: /* allocate head of pbuf chain into p */ - p = pbuf_pool_alloc(); - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + p = memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); if (p == NULL) { -#if PBUF_STATS - ++lwip_stats.pbuf.err; -#endif /* PBUF_STATS */ + PBUF_POOL_IS_EMPTY(); return NULL; } + p->type = type; p->next = NULL; /* make the payload pointer point 'offset' bytes into pbuf data memory */ - p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset))); + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); /* the total length of the pbuf chain is the requested size */ p->tot_len = length; /* set the length of the first pbuf in the chain */ - p->len = length > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: length; + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", + (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); /* set reference count (needed here in case we fail) */ p->ref = 1; @@ -270,27 +244,30 @@ pbuf_alloc(pbuf_layer l, u16_t length, p rem_len = length - p->len; /* any remaining pbufs to be allocated? */ while (rem_len > 0) { - q = pbuf_pool_alloc(); + q = memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { - LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n")); -#if PBUF_STATS - ++lwip_stats.pbuf.err; -#endif /* PBUF_STATS */ + PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ pbuf_free(p); /* bail out unsuccesfully */ return NULL; } + q->type = type; + q->flags = 0; q->next = NULL; /* make previous pbuf point to this pbuf */ r->next = q; /* set total length of this pbuf and next in chain */ - q->tot_len = rem_len; + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; /* this pbuf length is pool size, unless smaller sized tail */ - q->len = rem_len > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rem_len; - q->payload = (void *)((u8_t *)q + sizeof(struct pbuf)); + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); q->ref = 1; /* calculate remaining length to be allocated */ rem_len -= q->len; @@ -303,15 +280,15 @@ pbuf_alloc(pbuf_layer l, u16_t length, p 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) + offset) + MEM_ALIGN_SIZE(length)); + p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); if (p == NULL) { return NULL; } /* Set up internal structure of the pbuf. */ - p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf) + offset)); + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); p->len = p->tot_len = length; p->next = NULL; - p->flags = PBUF_FLAG_RAM; + p->type = type; LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); @@ -323,53 +300,30 @@ pbuf_alloc(pbuf_layer l, u16_t length, p /* only allocate memory for the pbuf structure */ p = memp_malloc(MEMP_PBUF); if (p == NULL) { - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", flag == PBUF_ROM?"ROM":"REF")); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); return NULL; } /* caller must set this field properly, afterwards */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; - p->flags = (flag == PBUF_ROM? PBUF_FLAG_ROM: PBUF_FLAG_REF); + p->type = type; break; default: - LWIP_ASSERT("pbuf_alloc: erroneous flag", 0); + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } /* set reference count */ p->ref = 1; - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; } -#if PBUF_STATS -#define DEC_PBUF_STATS do { --lwip_stats.pbuf.used; } while (0) -#else /* PBUF_STATS */ -#define DEC_PBUF_STATS -#endif /* PBUF_STATS */ - -#define PBUF_POOL_FAST_FREE(p) do { \ - p->next = pbuf_pool; \ - pbuf_pool = p; \ - DEC_PBUF_STATS; \ - } while (0) - -#if SYS_LIGHTWEIGHT_PROT -#define PBUF_POOL_FREE(p) do { \ - SYS_ARCH_DECL_PROTECT(old_level); \ - SYS_ARCH_PROTECT(old_level); \ - PBUF_POOL_FAST_FREE(p); \ - SYS_ARCH_UNPROTECT(old_level); \ - } while (0) -#else /* SYS_LIGHTWEIGHT_PROT */ -#define PBUF_POOL_FREE(p) do { \ - sys_sem_wait(pbuf_pool_free_sem); \ - PBUF_POOL_FAST_FREE(p); \ - sys_sem_signal(pbuf_pool_free_sem); \ - } while (0) -#endif /* SYS_LIGHTWEIGHT_PROT */ - /** * Shrink a pbuf chain to a desired length. * @@ -383,19 +337,20 @@ pbuf_alloc(pbuf_layer l, u16_t length, p * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. * @note May not be called on a packet queue. * - * @bug Cannot grow the size of a pbuf (chain) (yet). + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). */ void pbuf_realloc(struct pbuf *p, u16_t new_len) { struct pbuf *q; u16_t rem_len; /* remaining length */ - s16_t grow; + s32_t grow; - LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL || - p->flags == PBUF_FLAG_ROM || - p->flags == PBUF_FLAG_RAM || - p->flags == PBUF_FLAG_REF); + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); /* desired length larger than current length? */ if (new_len >= p->tot_len) { @@ -415,18 +370,21 @@ pbuf_realloc(struct pbuf *p, u16_t new_l /* decrease remaining length by pbuf length */ rem_len -= q->len; /* decrease total length indicator */ - q->tot_len += grow; + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; /* proceed to next pbuf in chain */ q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); } /* we have now reached the new last pbuf (in q) */ /* rem_len == desired length for pbuf q */ /* shrink allocated memory for PBUF_RAM */ /* (other types merely adjust their length fields */ - if ((q->flags == PBUF_FLAG_RAM) && (rem_len != q->len)) { + if ((q->type == PBUF_RAM) && (rem_len != q->len)) { /* reallocate and adjust the length of the pbuf that will be split */ - mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); + q = mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); + LWIP_ASSERT("mem_realloc give q == NULL", q != NULL); } /* adjust length fields for new last pbuf */ q->len = rem_len; @@ -450,7 +408,8 @@ pbuf_realloc(struct pbuf *p, u16_t new_l * * The ->payload, ->tot_len and ->len fields are adjusted. * - * @param hdr_size_inc Number of bytes to increment header size which + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which * increases the size of the pbuf. New space is on the front. * (Using a negative value decreases the header size.) * If hdr_size_inc is 0, this function does nothing and returns succesful. @@ -464,32 +423,54 @@ pbuf_realloc(struct pbuf *p, u16_t new_l u8_t pbuf_header(struct pbuf *p, s16_t header_size_increment) { + u16_t type; void *payload; + u16_t increment_magnitude; LWIP_ASSERT("p != NULL", p != NULL); - if ((header_size_increment == 0) || (p == NULL)) return 0; + if ((header_size_increment == 0) || (p == NULL)) + return 0; + if (header_size_increment < 0){ + increment_magnitude = -header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; /* remember current payload pointer */ payload = p->payload; /* pbuf types containing payloads? */ - if (p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_POOL) { + if (type == PBUF_RAM || type == PBUF_POOL) { /* set new payload pointer */ p->payload = (u8_t *)p->payload - header_size_increment; /* boundary check fails? */ - if ((u8_t *)p->payload < (u8_t *)p + sizeof(struct pbuf)) { - LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", - (void *)p->payload, - (void *)(p + 1)));\ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, (void *)(p + 1))); /* restore old payload pointer */ p->payload = payload; /* bail out unsuccesfully */ return 1; } /* pbuf types refering to external payloads? */ - } else if (p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_ROM) { + } else if (type == PBUF_REF || type == PBUF_ROM) { /* hide a header in the payload? */ - if ((header_size_increment < 0) && (header_size_increment - p->len <= 0)) { + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { /* increase payload pointer */ p->payload = (u8_t *)p->payload - header_size_increment; } else { @@ -498,11 +479,16 @@ pbuf_header(struct pbuf *p, s16_t header return 1; } } + else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } /* modify pbuf length fields */ p->len += header_size_increment; p->tot_len += header_size_increment; - LWIP_DEBUGF( PBUF_DEBUG, ("pbuf_header: old %p new %p (%"S16_F")\n", + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n", (void *)payload, (void *)p->payload, header_size_increment)); return 0; @@ -520,7 +506,7 @@ pbuf_header(struct pbuf *p, s16_t header * decrementing. So, when all reference counts are one, the whole * chain is free'd. * - * @param pbuf The pbuf (chain) to be dereferenced. + * @param p The pbuf (chain) to be dereferenced. * * @return the number of pbufs that were de-allocated * from the head of the chain. @@ -544,50 +530,53 @@ pbuf_header(struct pbuf *p, s16_t header u8_t pbuf_free(struct pbuf *p) { + u16_t type; struct pbuf *q; u8_t count; - SYS_ARCH_DECL_PROTECT(old_level); - LWIP_ASSERT("p != NULL", p != NULL); - /* if assertions are disabled, proceed with debug output */ if (p == NULL) { - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_free(p == NULL) was called.\n")); + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_free(p == NULL) was called.\n")); return 0; } - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_free(%p)\n", (void *)p)); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); PERF_START; - LWIP_ASSERT("pbuf_free: sane flags", - p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_ROM || - p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_POOL); + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL); count = 0; - /* Since decrementing ref cannot be guaranteed to be a single machine operation - * we must protect it. Also, the later test of ref must be protected. - */ - SYS_ARCH_PROTECT(old_level); /* de-allocate all consecutive pbufs from the head of the chain that * obtain a zero reference count after decrementing*/ while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); /* all pbufs in a chain are referenced at least once */ LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); /* decrease reference count (number of pointers to pbuf) */ - p->ref--; + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); /* this pbuf is no longer referenced to? */ - if (p->ref == 0) { + if (ref == 0) { /* remember next pbuf in chain for next iteration */ q = p->next; - LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: deallocating %p\n", (void *)p)); + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; /* is this a pbuf from the pool? */ - if (p->flags == PBUF_FLAG_POOL) { - p->len = p->tot_len = PBUF_POOL_BUFSIZE; - p->payload = (void *)((u8_t *)p + sizeof(struct pbuf)); - PBUF_POOL_FREE(p); + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); /* is this a ROM or RAM referencing pbuf? */ - } else if (p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) { + } else if (type == PBUF_ROM || type == PBUF_REF) { memp_free(MEMP_PBUF, p); - /* p->flags == PBUF_FLAG_RAM */ + /* type == PBUF_RAM */ } else { mem_free(p); } @@ -597,12 +586,11 @@ pbuf_free(struct pbuf *p) /* p->ref > 0, this pbuf is still referenced to */ /* (and so the remaining pbufs in chain as well) */ } else { - LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)p->ref)); + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); /* stop walking through the chain */ p = NULL; } } - SYS_ARCH_UNPROTECT(old_level); PERF_STOP("pbuf_free"); /* return number of de-allocated pbufs */ return count; @@ -661,9 +649,8 @@ pbuf_cat(struct pbuf *h, struct pbuf *t) { struct pbuf *p; - LWIP_ASSERT("h != NULL (programmer violates API)", h != NULL); - LWIP_ASSERT("t != NULL (programmer violates API)", t != NULL); - if ((h == NULL) || (t == NULL)) return; + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); /* proceed to last pbuf of chain */ for (p = h; p->next != NULL; p = p->next) { @@ -704,217 +691,7 @@ pbuf_chain(struct pbuf *h, struct pbuf * pbuf_cat(h, t); /* t is now referenced by h */ pbuf_ref(t); - LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); -} - -/* For packet queueing. Note that queued packets MUST be dequeued first - * using pbuf_dequeue() before calling other pbuf_() functions. */ -#if ARP_QUEUEING -/** - * Add a packet to the end of a queue. - * - * @param q pointer to first packet on the queue - * @param n packet to be queued - * - * Both packets MUST be given, and must be different. - */ -void -pbuf_queue(struct pbuf *p, struct pbuf *n) -{ -#if PBUF_DEBUG /* remember head of queue */ - struct pbuf *q = p; -#endif - /* programmer stupidity checks */ - LWIP_ASSERT("p == NULL in pbuf_queue: this indicates a programmer error\n", p != NULL); - LWIP_ASSERT("n == NULL in pbuf_queue: this indicates a programmer error\n", n != NULL); - LWIP_ASSERT("p == n in pbuf_queue: this indicates a programmer error\n", p != n); - if ((p == NULL) || (n == NULL) || (p == n)){ - LWIP_DEBUGF(PBUF_DEBUG | DBG_HALT | 3, ("pbuf_queue: programmer argument error\n")); - return; - } - - /* iterate through all packets on queue */ - while (p->next != NULL) { -/* be very picky about pbuf chain correctness */ -#if PBUF_DEBUG - /* iterate through all pbufs in packet */ - while (p->tot_len != p->len) { - /* make sure invariant condition holds */ - LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len); - /* make sure each packet is complete */ - LWIP_ASSERT("p->next != NULL", p->next != NULL); - p = p->next; - /* { p->tot_len == p->len => p is last pbuf of a packet } */ - } - /* { p is last pbuf of a packet } */ - /* proceed to next packet on queue */ -#endif - /* proceed to next pbuf */ - if (p->next != NULL) p = p->next; - } - /* { p->tot_len == p->len and p->next == NULL } ==> - * { p is last pbuf of last packet on queue } */ - /* chain last pbuf of queue with n */ - p->next = n; - /* n is now referenced to by the (packet p in the) queue */ - pbuf_ref(n); -#if PBUF_DEBUG - LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, - ("pbuf_queue: newly queued packet %p sits after packet %p in queue %p\n", - (void *)n, (void *)p, (void *)q)); -#endif -} - -/** - * Remove a packet from the head of a queue. - * - * The caller MUST reference the remainder of the queue (as returned). The - * caller MUST NOT call pbuf_ref() as it implicitly takes over the reference - * from p. - * - * @param p pointer to first packet on the queue which will be dequeued. - * @return first packet on the remaining queue (NULL if no further packets). - * - */ -struct pbuf * -pbuf_dequeue(struct pbuf *p) -{ - struct pbuf *q; - LWIP_ASSERT("p != NULL", p != NULL); - - /* iterate through all pbufs in packet p */ - while (p->tot_len != p->len) { - /* make sure invariant condition holds */ - LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len); - /* make sure each packet is complete */ - LWIP_ASSERT("p->next != NULL", p->next != NULL); - p = p->next; - } - /* { p->tot_len == p->len } => p is the last pbuf of the first packet */ - /* remember next packet on queue in q */ - q = p->next; - /* dequeue packet p from queue */ - p->next = NULL; - /* any next packet on queue? */ - if (q != NULL) { - /* although q is no longer referenced by p, it MUST be referenced by - * the caller, who is maintaining this packet queue. So, we do not call - * pbuf_free(q) here, resulting in an implicit pbuf_ref(q) for the caller. */ - LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: first remaining packet on queue is %p\n", (void *)q)); - } else { - LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: no further packets on queue\n")); - } - return q; -} -#endif - -/** - * - * Create PBUF_POOL (or PBUF_RAM) copies of PBUF_REF pbufs. - * - * Used to queue packets on behalf of the lwIP stack, such as - * ARP based queueing. - * - * Go through a pbuf chain and replace any PBUF_REF buffers - * with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of - * the referenced data. - * - * @note You MUST explicitly use p = pbuf_take(p); - * The pbuf you give as argument, may have been replaced - * by a (differently located) copy through pbuf_take()! - * - * @note Any replaced pbufs will be freed through pbuf_free(). - * This may deallocate them if they become no longer referenced. - * - * @param p Head of pbuf chain to process - * - * @return Pointer to head of pbuf chain - */ -struct pbuf * -pbuf_take(struct pbuf *p) -{ - struct pbuf *q , *prev, *head; - LWIP_ASSERT("pbuf_take: p != NULL\n", p != NULL); - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)\n", (void*)p)); - - prev = NULL; - head = p; - /* iterate through pbuf chain */ - do - { - /* pbuf is of type PBUF_REF? */ - if (p->flags == PBUF_FLAG_REF) { - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p\n", (void *)p)); - /* allocate a pbuf (w/ payload) fully in RAM */ - /* PBUF_POOL buffers are faster if we can use them */ - if (p->len <= PBUF_POOL_BUFSIZE) { - q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); - if (q == NULL) { - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL\n")); - } - } else { - /* no replacement pbuf yet */ - q = NULL; - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: PBUF_POOL too small to replace PBUF_REF\n")); - } - /* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ - if (q == NULL) { - q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); - if (q == NULL) { - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAM\n")); - } - } - /* replacement pbuf could be allocated? */ - if (q != NULL) - { - /* copy p to q */ - /* copy successor */ - q->next = p->next; - /* remove linkage from original pbuf */ - p->next = NULL; - /* remove linkage to original pbuf */ - if (prev != NULL) { - /* prev->next == p at this point */ - LWIP_ASSERT("prev->next == p", prev->next == p); - /* break chain and insert new pbuf instead */ - prev->next = q; - /* prev == NULL, so we replaced the head pbuf of the chain */ - } else { - head = q; - } - /* copy pbuf payload */ - memcpy(q->payload, p->payload, p->len); - q->tot_len = p->tot_len; - q->len = p->len; - /* in case p was the first pbuf, it is no longer refered to by - * our caller, as the caller MUST do p = pbuf_take(p); - * in case p was not the first pbuf, it is no longer refered to - * by prev. we can safely free the pbuf here. - * (note that we have set p->next to NULL already so that - * we will not free the rest of the chain by accident.) - */ - pbuf_free(p); - /* do not copy ref, since someone else might be using the old buffer */ - LWIP_DEBUGF(PBUF_DEBUG, ("pbuf_take: replaced PBUF_REF %p with %p\n", (void *)p, (void *)q)); - p = q; - } else { - /* deallocate chain */ - pbuf_free(head); - LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_take: failed to allocate replacement pbuf for %p\n", (void *)p)); - return NULL; - } - /* p->flags != PBUF_FLAG_REF */ - } else { - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: skipping pbuf not of type PBUF_REF\n")); - } - /* remember this pbuf */ - prev = p; - /* proceed to next pbuf in original chain */ - p = p->next; - } while (p); - LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: end of chain reached.\n")); - - return head; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); } /** @@ -943,15 +720,210 @@ pbuf_dechain(struct pbuf *p) /* total length of pbuf p is its own length only */ p->tot_len = p->len; /* q is no longer referenced by p, free it */ - LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); tail_gone = pbuf_free(q); if (tail_gone > 0) { - LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); } /* return remaining tail or NULL if deallocated */ } /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); - return (tail_gone > 0? NULL: q); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", + (void*)p_to, (void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + LWIP_ASSERT("p_to != NULL", p_to != NULL); + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + } + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + + if((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_from->next == NULL), return ERR_VAL;); + } + if((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; } + +/** + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough). No more + * than buf->tot_len will be copied, irrespective of len + * @param offset offset into the packet buffer from where to begin copying len bytes + * @return the number of bytes copied, or 0 on failure + */ +u16_t +pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) + buf_copy_len = len; + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} + +/** + * Copy application supplied data into a pbuf. + * This function can only be used to copy the equivalent of buf->tot_len data. + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) +{ + struct pbuf *p; + u16_t buf_copy_len; + u16_t total_copy_len = len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;); + + if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { + return ERR_ARG; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; total_copy_len != 0; p = p->next) { + LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); + buf_copy_len = total_copy_len; + if (buf_copy_len > p->len) { + /* this pbuf cannot hold all remaining data */ + buf_copy_len = p->len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len); + total_copy_len -= buf_copy_len; + copied_total += buf_copy_len; + } + LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); + return ERR_OK; +} + +/** + * Creates a single pbuf out of a queue of pbufs. + * + * @remark: The source pbuf 'p' is not freed by this function because that can + * be illegal in some places! + * + * @param p the source pbuf + * @param layer pbuf_layer of the new pbuf + * + * @return a new, single pbuf (p->next is NULL) + * or the old pbuf if allocation fails + */ +struct pbuf* +pbuf_coalesce(struct pbuf *p, pbuf_layer layer) +{ + struct pbuf *q; + err_t err; + if (p->next == NULL) { + return p; + } + q = pbuf_alloc(layer, p->tot_len, PBUF_RAM); + if (q == NULL) { + /* @todo: what do we do now? */ + return p; + } + err = pbuf_copy(q, p); + LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); + pbuf_free(p); + return q; +} diff --git a/packages/net/lwip_tcpip/current/src/core/raw.c b/packages/net/lwip_tcpip/current/src/core/raw.c --- a/packages/net/lwip_tcpip/current/src/core/raw.c +++ b/packages/net/lwip_tcpip/current/src/core/raw.c @@ -1,11 +1,11 @@ /** * @file - * * Implementation of raw protocol PCBs for low-level handling of * different types of protocols besides (or overriding) those * already available in lwIP. * */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -38,9 +38,9 @@ * */ -#include +#include "lwip/opt.h" -#include "lwip/opt.h" +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ #include "lwip/def.h" #include "lwip/memp.h" @@ -48,22 +48,14 @@ #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/raw.h" - #include "lwip/stats.h" - +#include "lwip/snmp.h" #include "arch/perf.h" -#include "lwip/snmp.h" -#if LWIP_RAW +#include /** The list of RAW PCBs */ -static struct raw_pcb *raw_pcbs = NULL; - -void -raw_init(void) -{ - raw_pcbs = NULL; -} +static struct raw_pcb *raw_pcbs; /** * Determine if in incoming IP packet is covered by a RAW PCB @@ -73,9 +65,9 @@ raw_init(void) * finds a corresponding RAW PCB and calls the corresponding receive * callback function. * - * @param pbuf pbuf to be demultiplexed to a RAW PCB. - * @param netif network interface on which the datagram was received. - * @Return - 1 if the packet has been eaten by a RAW PCB receive + * @param p pbuf to be demultiplexed to a RAW PCB. + * @param inp network interface on which the datagram was received. + * @return - 1 if the packet has been eaten by a RAW PCB receive * callback function. The caller MAY NOT not reference the * packet any longer, and MAY NOT call pbuf_free(). * @return - 0 if packet is not eaten (pbuf is still referenced by the @@ -85,32 +77,48 @@ raw_init(void) u8_t raw_input(struct pbuf *p, struct netif *inp) { - struct raw_pcb *pcb; + struct raw_pcb *pcb, *prev; struct ip_hdr *iphdr; s16_t proto; u8_t eaten = 0; + LWIP_UNUSED_ARG(inp); + iphdr = p->payload; proto = IPH_PROTO(iphdr); + prev = NULL; pcb = raw_pcbs; /* loop through all raw pcbs until the packet is eaten by one */ /* this allows multiple pcbs to match against the packet by design */ while ((eaten == 0) && (pcb != NULL)) { if (pcb->protocol == proto) { - /* receive callback function available? */ - if (pcb->recv != NULL) { - /* the receive callback function did not eat the packet? */ - if (pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src)) != 0) - { - /* receive function ate the packet */ - p = NULL; - eaten = 1; +#if IP_SOF_BROADCAST_RECV + /* broadcast filter? */ + if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(&(iphdr->dest), inp)) +#endif /* IP_SOF_BROADCAST_RECV */ + { + /* receive callback function available? */ + if (pcb->recv != NULL) { + /* the receive callback function did not eat the packet? */ + if (pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src)) != 0) { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + if (prev != NULL) { + /* move the pcb to the front of raw_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + } } + /* no receive callback function was set for this raw PCB */ } - /* no receive callback function was set for this raw PCB */ /* drop the packet */ } + prev = pcb; pcb = pcb->next; } return eaten; @@ -202,7 +210,7 @@ raw_sendto(struct raw_pcb *pcb, struct p struct ip_addr *src_ip; struct pbuf *q; /* q will be sent down the stack */ - LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 3, ("raw_sendto\n")); + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); /* not enough space to add an IP header to first pbuf in given p chain? */ if (pbuf_header(p, IP_HLEN)) { @@ -210,7 +218,7 @@ raw_sendto(struct raw_pcb *pcb, struct p q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { - LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 2, ("raw_sendto: could not allocate header\n")); + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); return ERR_MEM; } /* chain header q in front of given pbuf p */ @@ -220,14 +228,14 @@ raw_sendto(struct raw_pcb *pcb, struct p } else { /* first pbuf q equals given pbuf */ q = p; - pbuf_header(q, -IP_HLEN); + if(pbuf_header(q, -IP_HLEN)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } } - + if ((netif = ip_route(ipaddr)) == NULL) { - LWIP_DEBUGF(RAW_DEBUG | 1, ("raw_sendto: No route to 0x%"X32_F"\n", ipaddr->addr)); -#if RAW_STATS - /* ++lwip_stats.raw.rterr;*/ -#endif /* RAW_STATS */ + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to 0x%"X32_F"\n", ipaddr->addr)); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); @@ -235,6 +243,18 @@ raw_sendto(struct raw_pcb *pcb, struct p return ERR_RTE; } +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif) ) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + if (ip_addr_isany(&pcb->local_ip)) { /* use outgoing network interface IP address as source address */ src_ip = &(netif->ip_addr); @@ -243,7 +263,13 @@ raw_sendto(struct raw_pcb *pcb, struct p src_ip = &(pcb->local_ip); } +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ /* did we chain a header earlier? */ if (q != p) { @@ -258,7 +284,6 @@ raw_sendto(struct raw_pcb *pcb, struct p * * @param pcb the raw pcb which to send * @param p the IP payload to send - * @param ipaddr the destination address of the IP packet * */ err_t @@ -284,11 +309,13 @@ raw_remove(struct raw_pcb *pcb) /* make list start at 2nd pcb */ raw_pcbs = raw_pcbs->next; /* pcb not 1st in list */ - } else for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { - /* find pcb in raw_pcbs list */ - if (pcb2->next != NULL && pcb2->next == pcb) { - /* remove pcb from list */ - pcb2->next = pcb->next; + } else { + for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } } } memp_free(MEMP_RAW_PCB, pcb); @@ -305,10 +332,10 @@ raw_remove(struct raw_pcb *pcb) * @see raw_remove() */ struct raw_pcb * -raw_new(u16_t proto) { +raw_new(u8_t proto) { struct raw_pcb *pcb; - LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 3, ("raw_new\n")); + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); pcb = memp_malloc(MEMP_RAW_PCB); /* could allocate RAW PCB? */ diff --git a/packages/net/lwip_tcpip/current/src/core/snmp/asn1_dec.c b/packages/net/lwip_tcpip/current/src/core/snmp/asn1_dec.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/snmp/asn1_dec.c @@ -0,0 +1,657 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) decoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Retrieves type field from incoming pbuf chain. + * + * @param p points to a pbuf holding an ASN1 coded type field + * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field + * @param type return ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + *type = *msg_ptr; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes length field from incoming pbuf chain into host length. + * + * @param p points to a pbuf holding an ASN1 coded length + * @param ofs points to the offset within the pbuf chain of the ASN1 coded length + * @param octets_used returns number of octets used by the length code + * @param length return host order length, upto 64k + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (*msg_ptr < 0x80) + { + /* primitive definite length format */ + *octets_used = 1; + *length = *msg_ptr; + return ERR_OK; + } + else if (*msg_ptr == 0x80) + { + /* constructed indefinite length format, termination with two zero octets */ + u8_t zeros; + u8_t i; + + *length = 0; + zeros = 0; + while (zeros != 2) + { + i = 2; + while (i > 0) + { + i--; + (*length) += 1; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (*msg_ptr == 0) + { + zeros++; + if (zeros == 2) + { + /* stop while (i > 0) */ + i = 0; + } + } + else + { + zeros = 0; + } + } + } + *octets_used = 1; + return ERR_OK; + } + else if (*msg_ptr == 0x81) + { + /* constructed definite length format, one octet */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *length = *msg_ptr; + *octets_used = 2; + return ERR_OK; + } + else if (*msg_ptr == 0x82) + { + u8_t i; + + /* constructed definite length format, two octets */ + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *length |= *msg_ptr; + } + else + { + /* most significant length octet */ + *length = (*msg_ptr) << 8; + } + } + *octets_used = 3; + return ERR_OK; + } + else + { + /* constructed definite length format 3..127 octets, this is too big (>64k) */ + /** @todo: do we need to accept inefficient codings with many leading zero's? */ + *octets_used = 1 + ((*msg_ptr) & 0x7f); + return ERR_ARG; + } + } + p = p->next; + } + + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 6)) + { + /* start from zero */ + *value = 0; + if (*msg_ptr & 0x80) + { + /* negative, expecting zero sign bit! */ + return ERR_ARG; + } + else + { + /* positive */ + if ((len > 1) && (*msg_ptr == 0)) + { + /* skip leading "sign byte" octet 0x00 */ + len--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + } + /* OR octets with value */ + while (len > 1) + { + len--; + *value |= *msg_ptr; + *value <<= 8; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + *value |= *msg_ptr; + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes integer into s32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 5)) + { + if (*msg_ptr & 0x80) + { + /* negative, start from -1 */ + *value = -1; + sign = 1; + } + else + { + /* positive, start from 0 */ + *value = 0; + sign = 0; + } + /* OR/AND octets with value */ + while (len > 1) + { + len--; + if (sign) + { + *lsb_ptr &= *msg_ptr; + *value <<= 8; + *lsb_ptr |= 255; + } + else + { + *lsb_ptr |= *msg_ptr; + *value <<= 8; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (sign) + { + *lsb_ptr &= *msg_ptr; + } + else + { + *lsb_ptr |= *msg_ptr; + } + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes object identifier from incoming message into array of s32_t. + * + * @param p points to a pbuf holding an ASN1 coded object identifier + * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier + * @param len length of the coded object identifier + * @param oid return object identifier struct + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) +{ + u16_t plen, base; + u8_t *msg_ptr; + s32_t *oid_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + oid->len = 0; + oid_ptr = &oid->id[0]; + if (len > 0) + { + /* first compressed octet */ + if (*msg_ptr == 0x2B) + { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } + else if (*msg_ptr < 40) + { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = *msg_ptr; + oid_ptr++; + } + else if (*msg_ptr < 80) + { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 40; + oid_ptr++; + } + else + { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 80; + oid_ptr++; + } + oid->len = 2; + } + else + { + /* accepting zero length identifiers e.g. for + getnext operation. uncommon but valid */ + return ERR_OK; + } + len--; + if (len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) + { + /* sub-identifier uses multiple octets */ + if (*msg_ptr & 0x80) + { + s32_t sub_id = 0; + + while ((*msg_ptr & 0x80) && (len > 1)) + { + len--; + sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (!(*msg_ptr & 0x80) && (len > 0)) + { + /* last octet sub-identifier */ + len--; + sub_id = (sub_id << 7) + *msg_ptr; + *oid_ptr = sub_id; + } + } + else + { + /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ + len--; + *oid_ptr = *msg_ptr; + } + if (len > 0) + { + /* remaining oid bytes available ... */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + oid_ptr++; + oid->len++; + } + if (len == 0) + { + /* len == 0, end of oid */ + return ERR_OK; + } + else + { + /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ + return ERR_ARG; + } + + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param p points to a pbuf holding an ASN1 coded raw data + * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param raw_len length of the raw return value + * @param raw return raw bytes + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + if (len > 0) + { + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if (raw_len >= len) + { + while (len > 1) + { + /* copy len - 1 octets */ + len--; + *raw = *msg_ptr; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* copy last octet */ + *raw = *msg_ptr; + return ERR_OK; + } + else + { + /* raw_len < len, not enough dst space */ + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; + } + else + { + /* len == 0, empty string */ + return ERR_OK; + } +} + +#endif /* LWIP_SNMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/snmp/asn1_enc.c b/packages/net/lwip_tcpip/current/src/core/snmp/asn1_enc.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/snmp/asn1_enc.c @@ -0,0 +1,611 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Returns octet count for length. + * + * @param length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) + { + *octets_needed = 1; + } + else if (length < 0x100U) + { + *octets_needed = 2; + } + else + { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) + { + *octets_needed = 1; + } + else if (value < 0x8000UL) + { + *octets_needed = 2; + } + else if (value < 0x800000UL) + { + *octets_needed = 3; + } + else if (value < 0x80000000UL) + { + *octets_needed = 4; + } + else + { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) + { + value = ~value; + } + if (value < 0x80L) + { + *octets_needed = 1; + } + else if (value < 0x8000L) + { + *octets_needed = 2; + } + else if (value < 0x800000L) + { + *octets_needed = 3; + } + else + { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) +{ + s32_t sub_id; + u8_t cnt; + + cnt = 0; + if (ident_len > 1) + { + /* compressed prefix in one octet */ + cnt++; + ident_len -= 2; + ident += 2; + } + while(ident_len > 0) + { + ident_len--; + sub_id = *ident; + + sub_id >>= 7; + cnt++; + while(sub_id > 0) + { + sub_id >>= 7; + cnt++; + } + ident++; + } + *octets_needed = cnt; +} + +/** + * Encodes ASN type field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param type input ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + *msg_ptr = type; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes host order length field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode length into + * @param ofs points to the offset within the pbuf chain + * @param length is the host order length to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (length < 0x80) + { + *msg_ptr = length; + return ERR_OK; + } + else if (length < 0x100) + { + *msg_ptr = 0x81; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *msg_ptr = length; + return ERR_OK; + } + else + { + u8_t i; + + /* length >= 0x100 && length <= 0xFFFF */ + *msg_ptr = 0x82; + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *msg_ptr = length; + } + else + { + /* most significant length octet */ + *msg_ptr = length >> 8; + } + } + return ERR_OK; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, u32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (octets_needed == 5) + { + /* not enough bits in 'value' add leading 0x00 */ + octets_needed--; + *msg_ptr = 0x00; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = value >> (octets_needed << 3); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, s32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = value >> (octets_needed << 3); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode oid into + * @param ofs points to the offset within the pbuf chain + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (ident_len > 1) + { + if ((ident[0] == 1) && (ident[1] == 3)) + { + /* compressed (most common) prefix .iso.org */ + *msg_ptr = 0x2b; + } + else + { + /* calculate prefix */ + *msg_ptr = (ident[0] * 40) + ident[1]; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + ident_len -= 2; + ident += 2; + } + else + { +/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + while (ident_len > 0) + { + s32_t sub_id; + u8_t shift, tail; + + ident_len--; + sub_id = *ident; + tail = 0; + shift = 28; + while(shift > 0) + { + u8_t code; + + code = sub_id >> shift; + if ((code != 0) || (tail != 0)) + { + tail = 1; + *msg_ptr = code | 0x80; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + shift -= 7; + } + *msg_ptr = (u8_t)sub_id & 0x7F; + if (ident_len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* proceed to next sub-identifier */ + ident++; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode raw data into + * @param ofs points to the offset within the pbuf chain + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u8_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + while (raw_len > 1) + { + /* copy raw_len - 1 octets */ + raw_len--; + *msg_ptr = *raw; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (raw_len > 0) + { + /* copy last or single octet */ + *msg_ptr = *raw; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +#endif /* LWIP_SNMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/snmp/mib2.c b/packages/net/lwip_tcpip/current/src/core/snmp/mib2.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/snmp/mib2.c @@ -0,0 +1,4128 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + * + * @note the object identifiers for this MIB-2 and private MIB tree + * must be kept in sorted ascending order. This to ensure correct getnext operation. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_structs.h" +#include "netif/etharp.h" + +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers (contact Christiaan Simons)! + * @note don't change this define, use snmp_set_sysobjid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_ENTERPRISE_ID 26381 +#define SNMP_SYSOBJID_LEN 7 +#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID} + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +#ifndef SNMP_GET_SYSUPTIME +#define SNMP_GET_SYSUPTIME(sysuptime) +#endif + +static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void system_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t system_set_test(struct obj_def *od, u16_t len, void *value); +static void system_set_value(struct obj_def *od, u16_t len, void *value); +static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void interfaces_get_value(struct obj_def *od, u16_t len, void *value); +static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ifentry_get_value(struct obj_def *od, u16_t len, void *value); +#if !SNMP_SAFE_REQUESTS +static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value); +static void ifentry_set_value (struct obj_def *od, u16_t len, void *value); +#endif /* SNMP_SAFE_REQUESTS */ +static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void atentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value); +static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value); +static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void icmp_get_value(struct obj_def *od, u16_t len, void *value); +#if LWIP_TCP +static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcp_get_value(struct obj_def *od, u16_t len, void *value); +#ifdef THIS_SEEMS_UNUSED +static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value); +#endif +#endif +static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udp_get_value(struct obj_def *od, u16_t len, void *value); +static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udpentry_get_value(struct obj_def *od, u16_t len, void *value); +static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void snmp_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value); +static void snmp_set_value(struct obj_def *od, u16_t len, void *value); + + +/* snmp .1.3.6.1.2.1.11 */ +const mib_scalar_node snmp_scalar = { + &snmp_get_object_def, + &snmp_get_value, + &snmp_set_test, + &snmp_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t snmp_ids[28] = { + 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30 +}; +struct mib_node* const snmp_nodes[28] = { + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar +}; +const struct mib_array_node snmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 28, + snmp_ids, + snmp_nodes +}; + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* udp .1.3.6.1.2.1.7 */ +/** index root node for udpTable */ +struct mib_list_rootnode udp_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t udpentry_ids[2] = { 1, 2 }; +struct mib_node* const udpentry_nodes[2] = { + (struct mib_node* const)&udp_root, (struct mib_node* const)&udp_root, +}; +const struct mib_array_node udpentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + udpentry_ids, + udpentry_nodes +}; + +s32_t udptable_id = 1; +struct mib_node* udptable_node = (struct mib_node* const)&udpentry; +struct mib_ram_array_node udptable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &udptable_id, + &udptable_node +}; + +const mib_scalar_node udp_scalar = { + &udp_get_object_def, + &udp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const udp_nodes[5] = { + (struct mib_node* const)&udp_scalar, (struct mib_node* const)&udp_scalar, + (struct mib_node* const)&udp_scalar, (struct mib_node* const)&udp_scalar, + (struct mib_node* const)&udptable +}; +const struct mib_array_node udp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + udp_ids, + udp_nodes +}; + +/* tcp .1.3.6.1.2.1.6 */ +#if LWIP_TCP +/* only if the TCP protocol is available may implement this group */ +/** index root node for tcpConnTable */ +struct mib_list_rootnode tcpconntree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const tcpconnentry_nodes[5] = { + (struct mib_node* const)&tcpconntree_root, (struct mib_node* const)&tcpconntree_root, + (struct mib_node* const)&tcpconntree_root, (struct mib_node* const)&tcpconntree_root, + (struct mib_node* const)&tcpconntree_root +}; +const struct mib_array_node tcpconnentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + tcpconnentry_ids, + tcpconnentry_nodes +}; + +s32_t tcpconntable_id = 1; +struct mib_node* tcpconntable_node = (struct mib_node* const)&tcpconnentry; +struct mib_ram_array_node tcpconntable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, +/** @todo update maxlength when inserting / deleting from table + 0 when table is empty, 1 when more than one entry */ + 0, + &tcpconntable_id, + &tcpconntable_node +}; + +const mib_scalar_node tcp_scalar = { + &tcp_get_object_def, + &tcp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +struct mib_node* const tcp_nodes[15] = { + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcpconntable, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar +}; +const struct mib_array_node tcp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 15, + tcp_ids, + tcp_nodes +}; +#endif + +/* icmp .1.3.6.1.2.1.5 */ +const mib_scalar_node icmp_scalar = { + &icmp_get_object_def, + &icmp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t icmp_ids[26] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; +struct mib_node* const icmp_nodes[26] = { + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar +}; +const struct mib_array_node icmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 26, + icmp_ids, + icmp_nodes +}; + +/** index root node for ipNetToMediaTable */ +struct mib_list_rootnode ipntomtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 }; +struct mib_node* const ipntomentry_nodes[4] = { + (struct mib_node* const)&ipntomtree_root, (struct mib_node* const)&ipntomtree_root, + (struct mib_node* const)&ipntomtree_root, (struct mib_node* const)&ipntomtree_root +}; +const struct mib_array_node ipntomentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 4, + ipntomentry_ids, + ipntomentry_nodes +}; + +s32_t ipntomtable_id = 1; +struct mib_node* ipntomtable_node = (struct mib_node* const)&ipntomentry; +struct mib_ram_array_node ipntomtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipntomtable_id, + &ipntomtable_node +}; + +/** index root node for ipRouteTable */ +struct mib_list_rootnode iprtetree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; +struct mib_node* const iprteentry_nodes[13] = { + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root +}; +const struct mib_array_node iprteentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 13, + iprteentry_ids, + iprteentry_nodes +}; + +s32_t iprtetable_id = 1; +struct mib_node* iprtetable_node = (struct mib_node* const)&iprteentry; +struct mib_ram_array_node iprtetable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iprtetable_id, + &iprtetable_node +}; + +/** index root node for ipAddrTable */ +struct mib_list_rootnode ipaddrtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const ipaddrentry_nodes[5] = { + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root +}; +const struct mib_array_node ipaddrentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + ipaddrentry_ids, + ipaddrentry_nodes +}; + +s32_t ipaddrtable_id = 1; +struct mib_node* ipaddrtable_node = (struct mib_node* const)&ipaddrentry; +struct mib_ram_array_node ipaddrtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipaddrtable_id, + &ipaddrtable_node +}; + +/* ip .1.3.6.1.2.1.4 */ +const mib_scalar_node ip_scalar = { + &ip_get_object_def, + &ip_get_value, + &ip_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +struct mib_node* const ip_nodes[23] = { + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ipaddrtable, + (struct mib_node* const)&iprtetable, (struct mib_node* const)&ipntomtable, + (struct mib_node* const)&ip_scalar +}; +const struct mib_array_node mib2_ip = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 23, + ip_ids, + ip_nodes +}; + +/** index root node for atTable */ +struct mib_list_rootnode arptree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t atentry_ids[3] = { 1, 2, 3 }; +struct mib_node* const atentry_nodes[3] = { + (struct mib_node* const)&arptree_root, + (struct mib_node* const)&arptree_root, + (struct mib_node* const)&arptree_root +}; +const struct mib_array_node atentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 3, + atentry_ids, + atentry_nodes +}; + +const s32_t attable_id = 1; +struct mib_node* const attable_node = (struct mib_node* const)&atentry; +const struct mib_array_node attable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + &attable_id, + &attable_node +}; + +/* at .1.3.6.1.2.1.3 */ +s32_t at_id = 1; +struct mib_node* mib2_at_node = (struct mib_node* const)&attable; +struct mib_ram_array_node at = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &at_id, + &mib2_at_node +}; + +/** index root node for ifTable */ +struct mib_list_rootnode iflist_root = { + &ifentry_get_object_def, + &ifentry_get_value, +#if SNMP_SAFE_REQUESTS + &noleafs_set_test, + &noleafs_set_value, +#else /* SNMP_SAFE_REQUESTS */ + &ifentry_set_test, + &ifentry_set_value, +#endif /* SNMP_SAFE_REQUESTS */ + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; +struct mib_node* const ifentry_nodes[22] = { + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root +}; +const struct mib_array_node ifentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 22, + ifentry_ids, + ifentry_nodes +}; + +s32_t iftable_id = 1; +struct mib_node* iftable_node = (struct mib_node* const)&ifentry; +struct mib_ram_array_node iftable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iftable_id, + &iftable_node +}; + +/* interfaces .1.3.6.1.2.1.2 */ +const mib_scalar_node interfaces_scalar = { + &interfaces_get_object_def, + &interfaces_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t interfaces_ids[2] = { 1, 2 }; +struct mib_node* const interfaces_nodes[2] = { + (struct mib_node* const)&interfaces_scalar, (struct mib_node* const)&iftable +}; +const struct mib_array_node interfaces = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + interfaces_ids, + interfaces_nodes +}; + + +/* 0 1 2 3 4 5 6 */ +/* system .1.3.6.1.2.1.1 */ +const mib_scalar_node sys_tem_scalar = { + &system_get_object_def, + &system_get_value, + &system_set_test, + &system_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 }; +struct mib_node* const sys_tem_nodes[7] = { + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar +}; +/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */ +const struct mib_array_node sys_tem = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 7, + sys_tem_ids, + sys_tem_nodes +}; + +/* mib-2 .1.3.6.1.2.1 */ +#if LWIP_TCP +#define MIB2_GROUPS 8 +#else +#define MIB2_GROUPS 7 +#endif +const s32_t mib2_ids[MIB2_GROUPS] = +{ + 1, + 2, + 3, + 4, + 5, +#if LWIP_TCP + 6, +#endif + 7, + 11 +}; +struct mib_node* const mib2_nodes[MIB2_GROUPS] = { + (struct mib_node* const)&sys_tem, + (struct mib_node* const)&interfaces, + (struct mib_node* const)&at, + (struct mib_node* const)&mib2_ip, + (struct mib_node* const)&icmp, +#if LWIP_TCP + (struct mib_node* const)&tcp, +#endif + (struct mib_node* const)&udp, + (struct mib_node* const)&snmp +}; + +const struct mib_array_node mib2 = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + MIB2_GROUPS, + mib2_ids, + mib2_nodes +}; + +/* mgmt .1.3.6.1.2 */ +const s32_t mgmt_ids[1] = { 1 }; +struct mib_node* const mgmt_nodes[1] = { (struct mib_node* const)&mib2 }; +const struct mib_array_node mgmt = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + mgmt_ids, + mgmt_nodes +}; + +/* internet .1.3.6.1 */ +#if SNMP_PRIVATE_MIB +s32_t internet_ids[2] = { 2, 4 }; +struct mib_node* const internet_nodes[2] = { (struct mib_node* const)&mgmt, (struct mib_node* const)&private }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + internet_ids, + internet_nodes +}; +#else +const s32_t internet_ids[1] = { 2 }; +struct mib_node* const internet_nodes[1] = { (struct mib_node* const)&mgmt }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + internet_ids, + internet_nodes +}; +#endif + +/** mib-2.system.sysObjectID */ +static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID}; +/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */ +static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}}; +/** mib-2.system.sysServices */ +static const s32_t sysservices = SNMP_SYSSERVICES; + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_len_default = 4; +static const u8_t sysdescr_default[] = "lwIP"; +static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default; +static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0]; +/** mib-2.system.sysContact */ +static const u8_t syscontact_len_default = 0; +static const u8_t syscontact_default[] = ""; +static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default; +static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0]; +/** mib-2.system.sysName */ +static const u8_t sysname_len_default = 8; +static const u8_t sysname_default[] = "FQDN-unk"; +static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default; +static u8_t* sysname_ptr = (u8_t*)&sysname_default[0]; +/** mib-2.system.sysLocation */ +static const u8_t syslocation_len_default = 0; +static const u8_t syslocation_default[] = ""; +static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default; +static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0]; +/** mib-2.snmp.snmpEnableAuthenTraps */ +static const u8_t snmpenableauthentraps_default = 2; /* disabled */ +static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default; + +/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */ +static const struct snmp_obj_id ifspecific = {2, {0, 0}}; +/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */ +static const struct snmp_obj_id iprouteinfo = {2, {0, 0}}; + + + +/* mib-2.system counter(s) */ +static u32_t sysuptime = 0; + +/* mib-2.ip counter(s) */ +static u32_t ipinreceives = 0, + ipinhdrerrors = 0, + ipinaddrerrors = 0, + ipforwdatagrams = 0, + ipinunknownprotos = 0, + ipindiscards = 0, + ipindelivers = 0, + ipoutrequests = 0, + ipoutdiscards = 0, + ipoutnoroutes = 0, + ipreasmreqds = 0, + ipreasmoks = 0, + ipreasmfails = 0, + ipfragoks = 0, + ipfragfails = 0, + ipfragcreates = 0, + iproutingdiscards = 0; +/* mib-2.icmp counter(s) */ +static u32_t icmpinmsgs = 0, + icmpinerrors = 0, + icmpindestunreachs = 0, + icmpintimeexcds = 0, + icmpinparmprobs = 0, + icmpinsrcquenchs = 0, + icmpinredirects = 0, + icmpinechos = 0, + icmpinechoreps = 0, + icmpintimestamps = 0, + icmpintimestampreps = 0, + icmpinaddrmasks = 0, + icmpinaddrmaskreps = 0, + icmpoutmsgs = 0, + icmpouterrors = 0, + icmpoutdestunreachs = 0, + icmpouttimeexcds = 0, + icmpoutparmprobs = 0, + icmpoutsrcquenchs = 0, + icmpoutredirects = 0, + icmpoutechos = 0, + icmpoutechoreps = 0, + icmpouttimestamps = 0, + icmpouttimestampreps = 0, + icmpoutaddrmasks = 0, + icmpoutaddrmaskreps = 0; +/* mib-2.tcp counter(s) */ +static u32_t tcpactiveopens = 0, + tcppassiveopens = 0, + tcpattemptfails = 0, + tcpestabresets = 0, + tcpinsegs = 0, + tcpoutsegs = 0, + tcpretranssegs = 0, + tcpinerrs = 0, + tcpoutrsts = 0; +/* mib-2.udp counter(s) */ +static u32_t udpindatagrams = 0, + udpnoports = 0, + udpinerrors = 0, + udpoutdatagrams = 0; +/* mib-2.snmp counter(s) */ +static u32_t snmpinpkts = 0, + snmpoutpkts = 0, + snmpinbadversions = 0, + snmpinbadcommunitynames = 0, + snmpinbadcommunityuses = 0, + snmpinasnparseerrs = 0, + snmpintoobigs = 0, + snmpinnosuchnames = 0, + snmpinbadvalues = 0, + snmpinreadonlys = 0, + snmpingenerrs = 0, + snmpintotalreqvars = 0, + snmpintotalsetvars = 0, + snmpingetrequests = 0, + snmpingetnexts = 0, + snmpinsetrequests = 0, + snmpingetresponses = 0, + snmpintraps = 0, + snmpouttoobigs = 0, + snmpoutnosuchnames = 0, + snmpoutbadvalues = 0, + snmpoutgenerrs = 0, + snmpoutgetrequests = 0, + snmpoutgetnexts = 0, + snmpoutsetrequests = 0, + snmpoutgetresponses = 0, + snmpouttraps = 0; + + + +/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */ +/** + * Copy octet string. + * + * @param dst points to destination + * @param src points to source + * @param n number of octets to copy. + */ +void ocstrncpy(u8_t *dst, u8_t *src, u8_t n) +{ + while (n > 0) + { + n--; + *dst++ = *src++; + } +} + +/** + * Copy object identifier (s32_t) array. + * + * @param dst points to destination + * @param src points to source + * @param n number of sub identifiers to copy. + */ +void objectidncpy(s32_t *dst, s32_t *src, u8_t n) +{ + while(n > 0) + { + n--; + *dst++ = *src++; + } +} + +/** + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void snmp_set_sysdesr(u8_t *str, u8_t *len) +{ + if (str != NULL) + { + sysdescr_ptr = str; + sysdescr_len_ptr = len; + } +} + +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid) +{ + *oid = &sysobjid; +} + +/** + * Initializes sysObjectID value. + * + * @param oid points to stuct snmp_obj_id to copy + */ +void snmp_set_sysobjid(struct snmp_obj_id *oid) +{ + sysobjid = *oid; +} + +/** + * Must be called at regular 10 msec interval from a timer interrupt + * or signal handler depending on your runtime environment. + */ +void snmp_inc_sysuptime(void) +{ + sysuptime++; +} + +void snmp_add_sysuptime(u32_t value) +{ + sysuptime+=value; +} + +void snmp_get_sysuptime(u32_t *value) +{ + SNMP_GET_SYSUPTIME(sysuptime); + *value = sysuptime; +} + +/** + * Initializes sysContact pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syscontact_ptr = ocstr; + syscontact_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysName pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + sysname_ptr = ocstr; + sysname_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysLocation pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syslocation_ptr = ocstr; + syslocation_len_ptr = ocstrlen; + } +} + + +void snmp_add_ifinoctets(struct netif *ni, u32_t value) +{ + ni->ifinoctets += value; +} + +void snmp_inc_ifinucastpkts(struct netif *ni) +{ + (ni->ifinucastpkts)++; +} + +void snmp_inc_ifinnucastpkts(struct netif *ni) +{ + (ni->ifinnucastpkts)++; +} + +void snmp_inc_ifindiscards(struct netif *ni) +{ + (ni->ifindiscards)++; +} + +void snmp_add_ifoutoctets(struct netif *ni, u32_t value) +{ + ni->ifoutoctets += value; +} + +void snmp_inc_ifoutucastpkts(struct netif *ni) +{ + (ni->ifoutucastpkts)++; +} + +void snmp_inc_ifoutnucastpkts(struct netif *ni) +{ + (ni->ifoutnucastpkts)++; +} + +void snmp_inc_ifoutdiscards(struct netif *ni) +{ + (ni->ifoutdiscards)++; +} + +void snmp_inc_iflist(void) +{ + struct mib_list_node *if_node = NULL; + + snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node); + /* enable getnext traversal on filled table */ + iftable.maxlength = 1; +} + +void snmp_dec_iflist(void) +{ + snmp_mib_node_delete(&iflist_root, iflist_root.tail); + /* disable getnext traversal on empty table */ + if(iflist_root.count == 0) iftable.maxlength = 0; +} + +/** + * Inserts ARP table indexes (.xIfIndex.xNetAddress) + * into arp table index trees (both atTable and ipNetToMediaTable). + */ +void snmp_insert_arpidx_tree(struct netif *ni, struct ip_addr *ip) +{ + struct mib_list_rootnode *at_rn; + struct mib_list_node *at_node; + struct ip_addr hip; + s32_t arpidx[5]; + u8_t level, tree; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_netiftoifindex(ni, &arpidx[0]); + hip.addr = ntohl(ip->addr); + snmp_iptooid(&hip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + for (level = 0; level < 5; level++) + { + at_node = NULL; + snmp_mib_node_insert(at_rn, arpidx[level], &at_node); + if ((level != 4) && (at_node != NULL)) + { + if (at_node->nptr == NULL) + { + at_rn = snmp_mib_lrn_alloc(); + at_node->nptr = (struct mib_node*)at_rn; + if (at_rn != NULL) + { + if (level == 3) + { + if (tree == 0) + { + at_rn->get_object_def = atentry_get_object_def; + at_rn->get_value = atentry_get_value; + } + else + { + at_rn->get_object_def = ip_ntomentry_get_object_def; + at_rn->get_value = ip_ntomentry_get_value; + } + at_rn->set_test = noleafs_set_test; + at_rn->set_value = noleafs_set_value; + } + } + else + { + /* at_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full")); + break; + } + } + else + { + at_rn = (struct mib_list_rootnode*)at_node->nptr; + } + } + } + } + /* enable getnext traversal on filled tables */ + at.maxlength = 1; + ipntomtable.maxlength = 1; +} + +/** + * Removes ARP table indexes (.xIfIndex.xNetAddress) + * from arp table index trees. + */ +void snmp_delete_arpidx_tree(struct netif *ni, struct ip_addr *ip) +{ + struct mib_list_rootnode *at_rn, *next, *del_rn[5]; + struct mib_list_node *at_n, *del_n[5]; + struct ip_addr hip; + s32_t arpidx[5]; + u8_t fc, tree, level, del_cnt; + + snmp_netiftoifindex(ni, &arpidx[0]); + hip.addr = ntohl(ip->addr); + snmp_iptooid(&hip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + /* mark nodes for deletion */ + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + level = 0; + del_cnt = 0; + while ((level < 5) && (at_rn != NULL)) + { + fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n); + if (fc == 0) + { + /* arpidx[level] does not exist */ + del_cnt = 0; + at_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = at_rn; + del_n[del_cnt] = at_n; + del_cnt++; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + at_rn = del_rn[del_cnt]; + at_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(at_rn, at_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty tables */ + if(arptree_root.count == 0) at.maxlength = 0; + if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0; +} + +void snmp_inc_ipinreceives(void) +{ + ipinreceives++; +} + +void snmp_inc_ipinhdrerrors(void) +{ + ipinhdrerrors++; +} + +void snmp_inc_ipinaddrerrors(void) +{ + ipinaddrerrors++; +} + +void snmp_inc_ipforwdatagrams(void) +{ + ipforwdatagrams++; +} + +void snmp_inc_ipinunknownprotos(void) +{ + ipinunknownprotos++; +} + +void snmp_inc_ipindiscards(void) +{ + ipindiscards++; +} + +void snmp_inc_ipindelivers(void) +{ + ipindelivers++; +} + +void snmp_inc_ipoutrequests(void) +{ + ipoutrequests++; +} + +void snmp_inc_ipoutdiscards(void) +{ + ipoutdiscards++; +} + +void snmp_inc_ipoutnoroutes(void) +{ + ipoutnoroutes++; +} + +void snmp_inc_ipreasmreqds(void) +{ + ipreasmreqds++; +} + +void snmp_inc_ipreasmoks(void) +{ + ipreasmoks++; +} + +void snmp_inc_ipreasmfails(void) +{ + ipreasmfails++; +} + +void snmp_inc_ipfragoks(void) +{ + ipfragoks++; +} + +void snmp_inc_ipfragfails(void) +{ + ipfragfails++; +} + +void snmp_inc_ipfragcreates(void) +{ + ipfragcreates++; +} + +void snmp_inc_iproutingdiscards(void) +{ + iproutingdiscards++; +} + +/** + * Inserts ipAddrTable indexes (.ipAdEntAddr) + * into index tree. + */ +void snmp_insert_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn; + struct mib_list_node *ipa_node; + struct ip_addr ip; + s32_t ipaddridx[4]; + u8_t level; + + LWIP_ASSERT("ni != NULL", ni != NULL); + ip.addr = ntohl(ni->ip_addr.addr); + snmp_iptooid(&ip, &ipaddridx[0]); + + level = 0; + ipa_rn = &ipaddrtree_root; + while (level < 4) + { + ipa_node = NULL; + snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node); + if ((level != 3) && (ipa_node != NULL)) + { + if (ipa_node->nptr == NULL) + { + ipa_rn = snmp_mib_lrn_alloc(); + ipa_node->nptr = (struct mib_node*)ipa_rn; + if (ipa_rn != NULL) + { + if (level == 2) + { + ipa_rn->get_object_def = ip_addrentry_get_object_def; + ipa_rn->get_value = ip_addrentry_get_value; + ipa_rn->set_test = noleafs_set_test; + ipa_rn->set_value = noleafs_set_value; + } + } + else + { + /* ipa_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full")); + break; + } + } + else + { + ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr; + } + } + level++; + } + /* enable getnext traversal on filled table */ + ipaddrtable.maxlength = 1; +} + +/** + * Removes ipAddrTable indexes (.ipAdEntAddr) + * from index tree. + */ +void snmp_delete_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn, *next, *del_rn[4]; + struct mib_list_node *ipa_n, *del_n[4]; + struct ip_addr ip; + s32_t ipaddridx[4]; + u8_t fc, level, del_cnt; + + LWIP_ASSERT("ni != NULL", ni != NULL); + ip.addr = ntohl(ni->ip_addr.addr); + snmp_iptooid(&ip, &ipaddridx[0]); + + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + ipa_rn = &ipaddrtree_root; + while ((level < 4) && (ipa_rn != NULL)) + { + fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n); + if (fc == 0) + { + /* ipaddridx[level] does not exist */ + del_cnt = 0; + ipa_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = ipa_rn; + del_n[del_cnt] = ipa_n; + del_cnt++; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + ipa_rn = del_rn[del_cnt]; + ipa_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(ipa_rn, ipa_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + /* disable getnext traversal on empty table */ + if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0; +} + +/** + * Inserts ipRouteTable indexes (.ipRouteDest) + * into index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte + * + * @todo record sysuptime for _this_ route when it is installed + * (needed for ipRouteAge) in the netif. + */ +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t insert = 0; + struct ip_addr dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + dst.addr = 0; + insert = 1; + } + else + { + /* route to the network address */ + dst.addr = ntohl(ni->ip_addr.addr & ni->netmask.addr); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (dst.addr != 0) insert = 1; + } + if (insert) + { + struct mib_list_rootnode *iprte_rn; + struct mib_list_node *iprte_node; + s32_t iprteidx[4]; + u8_t level; + + snmp_iptooid(&dst, &iprteidx[0]); + level = 0; + iprte_rn = &iprtetree_root; + while (level < 4) + { + iprte_node = NULL; + snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node); + if ((level != 3) && (iprte_node != NULL)) + { + if (iprte_node->nptr == NULL) + { + iprte_rn = snmp_mib_lrn_alloc(); + iprte_node->nptr = (struct mib_node*)iprte_rn; + if (iprte_rn != NULL) + { + if (level == 2) + { + iprte_rn->get_object_def = ip_rteentry_get_object_def; + iprte_rn->get_value = ip_rteentry_get_value; + iprte_rn->set_test = noleafs_set_test; + iprte_rn->set_value = noleafs_set_value; + } + } + else + { + /* iprte_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full")); + break; + } + } + else + { + iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr; + } + } + level++; + } + } + /* enable getnext traversal on filled table */ + iprtetable.maxlength = 1; +} + +/** + * Removes ipRouteTable indexes (.ipRouteDest) + * from index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte or NULL + * for default route to be removed. + */ +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t delete = 0; + struct ip_addr dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + dst.addr = 0; + delete = 1; + } + else + { + /* route to the network address */ + dst.addr = ntohl(ni->ip_addr.addr & ni->netmask.addr); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (dst.addr != 0) delete = 1; + } + if (delete) + { + struct mib_list_rootnode *iprte_rn, *next, *del_rn[4]; + struct mib_list_node *iprte_n, *del_n[4]; + s32_t iprteidx[4]; + u8_t fc, level, del_cnt; + + snmp_iptooid(&dst, &iprteidx[0]); + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + iprte_rn = &iprtetree_root; + while ((level < 4) && (iprte_rn != NULL)) + { + fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n); + if (fc == 0) + { + /* iprteidx[level] does not exist */ + del_cnt = 0; + iprte_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = iprte_rn; + del_n[del_cnt] = iprte_n; + del_cnt++; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + iprte_rn = del_rn[del_cnt]; + iprte_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(iprte_rn, iprte_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (iprtetree_root.count == 0) iprtetable.maxlength = 0; +} + + +void snmp_inc_icmpinmsgs(void) +{ + icmpinmsgs++; +} + +void snmp_inc_icmpinerrors(void) +{ + icmpinerrors++; +} + +void snmp_inc_icmpindestunreachs(void) +{ + icmpindestunreachs++; +} + +void snmp_inc_icmpintimeexcds(void) +{ + icmpintimeexcds++; +} + +void snmp_inc_icmpinparmprobs(void) +{ + icmpinparmprobs++; +} + +void snmp_inc_icmpinsrcquenchs(void) +{ + icmpinsrcquenchs++; +} + +void snmp_inc_icmpinredirects(void) +{ + icmpinredirects++; +} + +void snmp_inc_icmpinechos(void) +{ + icmpinechos++; +} + +void snmp_inc_icmpinechoreps(void) +{ + icmpinechoreps++; +} + +void snmp_inc_icmpintimestamps(void) +{ + icmpintimestamps++; +} + +void snmp_inc_icmpintimestampreps(void) +{ + icmpintimestampreps++; +} + +void snmp_inc_icmpinaddrmasks(void) +{ + icmpinaddrmasks++; +} + +void snmp_inc_icmpinaddrmaskreps(void) +{ + icmpinaddrmaskreps++; +} + +void snmp_inc_icmpoutmsgs(void) +{ + icmpoutmsgs++; +} + +void snmp_inc_icmpouterrors(void) +{ + icmpouterrors++; +} + +void snmp_inc_icmpoutdestunreachs(void) +{ + icmpoutdestunreachs++; +} + +void snmp_inc_icmpouttimeexcds(void) +{ + icmpouttimeexcds++; +} + +void snmp_inc_icmpoutparmprobs(void) +{ + icmpoutparmprobs++; +} + +void snmp_inc_icmpoutsrcquenchs(void) +{ + icmpoutsrcquenchs++; +} + +void snmp_inc_icmpoutredirects(void) +{ + icmpoutredirects++; +} + +void snmp_inc_icmpoutechos(void) +{ + icmpoutechos++; +} + +void snmp_inc_icmpoutechoreps(void) +{ + icmpoutechoreps++; +} + +void snmp_inc_icmpouttimestamps(void) +{ + icmpouttimestamps++; +} + +void snmp_inc_icmpouttimestampreps(void) +{ + icmpouttimestampreps++; +} + +void snmp_inc_icmpoutaddrmasks(void) +{ + icmpoutaddrmasks++; +} + +void snmp_inc_icmpoutaddrmaskreps(void) +{ + icmpoutaddrmaskreps++; +} + +void snmp_inc_tcpactiveopens(void) +{ + tcpactiveopens++; +} + +void snmp_inc_tcppassiveopens(void) +{ + tcppassiveopens++; +} + +void snmp_inc_tcpattemptfails(void) +{ + tcpattemptfails++; +} + +void snmp_inc_tcpestabresets(void) +{ + tcpestabresets++; +} + +void snmp_inc_tcpinsegs(void) +{ + tcpinsegs++; +} + +void snmp_inc_tcpoutsegs(void) +{ + tcpoutsegs++; +} + +void snmp_inc_tcpretranssegs(void) +{ + tcpretranssegs++; +} + +void snmp_inc_tcpinerrs(void) +{ + tcpinerrs++; +} + +void snmp_inc_tcpoutrsts(void) +{ + tcpoutrsts++; +} + +void snmp_inc_udpindatagrams(void) +{ + udpindatagrams++; +} + +void snmp_inc_udpnoports(void) +{ + udpnoports++; +} + +void snmp_inc_udpinerrors(void) +{ + udpinerrors++; +} + +void snmp_inc_udpoutdatagrams(void) +{ + udpoutdatagrams++; +} + +/** + * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort) + * into index tree. + */ +void snmp_insert_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn; + struct mib_list_node *udp_node; + struct ip_addr ip; + s32_t udpidx[5]; + u8_t level; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + ip.addr = ntohl(pcb->local_ip.addr); + snmp_iptooid(&ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + udp_rn = &udp_root; + for (level = 0; level < 5; level++) + { + udp_node = NULL; + snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node); + if ((level != 4) && (udp_node != NULL)) + { + if (udp_node->nptr == NULL) + { + udp_rn = snmp_mib_lrn_alloc(); + udp_node->nptr = (struct mib_node*)udp_rn; + if (udp_rn != NULL) + { + if (level == 3) + { + udp_rn->get_object_def = udpentry_get_object_def; + udp_rn->get_value = udpentry_get_value; + udp_rn->set_test = noleafs_set_test; + udp_rn->set_value = noleafs_set_value; + } + } + else + { + /* udp_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full")); + break; + } + } + else + { + udp_rn = (struct mib_list_rootnode*)udp_node->nptr; + } + } + } + udptable.maxlength = 1; +} + +/** + * Removes udpTable indexes (.udpLocalAddress.udpLocalPort) + * from index tree. + */ +void snmp_delete_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn, *next, *del_rn[5]; + struct mib_list_node *udp_n, *del_n[5]; + struct ip_addr ip; + s32_t udpidx[5]; + u8_t bindings, fc, level, del_cnt; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + ip.addr = ntohl(pcb->local_ip.addr); + snmp_iptooid(&ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + /* count PCBs for a given binding + (e.g. when reusing ports or for temp output PCBs) */ + bindings = 0; + pcb = udp_pcbs; + while ((pcb != NULL)) + { + if ((pcb->local_ip.addr == ip.addr) && + (pcb->local_port == udpidx[4])) + { + bindings++; + } + pcb = pcb->next; + } + if (bindings == 1) + { + /* selectively remove */ + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + udp_rn = &udp_root; + while ((level < 5) && (udp_rn != NULL)) + { + fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n); + if (fc == 0) + { + /* udpidx[level] does not exist */ + del_cnt = 0; + udp_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = udp_rn; + del_n[del_cnt] = udp_n; + del_cnt++; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + udp_rn = del_rn[del_cnt]; + udp_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(udp_rn, udp_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (udp_root.count == 0) udptable.maxlength = 0; +} + + +void snmp_inc_snmpinpkts(void) +{ + snmpinpkts++; +} + +void snmp_inc_snmpoutpkts(void) +{ + snmpoutpkts++; +} + +void snmp_inc_snmpinbadversions(void) +{ + snmpinbadversions++; +} + +void snmp_inc_snmpinbadcommunitynames(void) +{ + snmpinbadcommunitynames++; +} + +void snmp_inc_snmpinbadcommunityuses(void) +{ + snmpinbadcommunityuses++; +} + +void snmp_inc_snmpinasnparseerrs(void) +{ + snmpinasnparseerrs++; +} + +void snmp_inc_snmpintoobigs(void) +{ + snmpintoobigs++; +} + +void snmp_inc_snmpinnosuchnames(void) +{ + snmpinnosuchnames++; +} + +void snmp_inc_snmpinbadvalues(void) +{ + snmpinbadvalues++; +} + +void snmp_inc_snmpinreadonlys(void) +{ + snmpinreadonlys++; +} + +void snmp_inc_snmpingenerrs(void) +{ + snmpingenerrs++; +} + +void snmp_add_snmpintotalreqvars(u8_t value) +{ + snmpintotalreqvars += value; +} + +void snmp_add_snmpintotalsetvars(u8_t value) +{ + snmpintotalsetvars += value; +} + +void snmp_inc_snmpingetrequests(void) +{ + snmpingetrequests++; +} + +void snmp_inc_snmpingetnexts(void) +{ + snmpingetnexts++; +} + +void snmp_inc_snmpinsetrequests(void) +{ + snmpinsetrequests++; +} + +void snmp_inc_snmpingetresponses(void) +{ + snmpingetresponses++; +} + +void snmp_inc_snmpintraps(void) +{ + snmpintraps++; +} + +void snmp_inc_snmpouttoobigs(void) +{ + snmpouttoobigs++; +} + +void snmp_inc_snmpoutnosuchnames(void) +{ + snmpoutnosuchnames++; +} + +void snmp_inc_snmpoutbadvalues(void) +{ + snmpoutbadvalues++; +} + +void snmp_inc_snmpoutgenerrs(void) +{ + snmpoutgenerrs++; +} + +void snmp_inc_snmpoutgetrequests(void) +{ + snmpoutgetrequests++; +} + +void snmp_inc_snmpoutgetnexts(void) +{ + snmpoutgetnexts++; +} + +void snmp_inc_snmpoutsetrequests(void) +{ + snmpoutsetrequests++; +} + +void snmp_inc_snmpoutgetresponses(void) +{ + snmpoutgetresponses++; +} + +void snmp_inc_snmpouttraps(void) +{ + snmpouttraps++; +} + +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid) +{ + *oid = &snmpgrp_id; +} + +void snmp_set_snmpenableauthentraps(u8_t *value) +{ + if (value != NULL) + { + snmpenableauthentraps_ptr = value; + } +} + +void snmp_get_snmpenableauthentraps(u8_t *value) +{ + *value = *snmpenableauthentraps_ptr; +} + +void +noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + LWIP_UNUSED_ARG(ident_len); + LWIP_UNUSED_ARG(ident); + od->instance = MIB_OBJECT_NONE; +} + +void +noleafs_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + +u8_t +noleafs_set_test(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* can't set */ + return 0; +} + +void +noleafs_set_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + + +/** + * Returns systems object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param od points to object definition. + */ +static void +system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* sysDescr */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysdescr_len_ptr; + break; + case 2: /* sysObjectID */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = sysobjid.len * sizeof(s32_t); + break; + case 3: /* sysUpTime */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 4: /* sysContact */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syscontact_len_ptr; + break; + case 5: /* sysName */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysname_len_ptr; + break; + case 6: /* sysLocation */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syslocation_len_ptr; + break; + case 7: /* sysServices */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns system object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +system_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* sysDescr */ + ocstrncpy(value,sysdescr_ptr, len); + break; + case 2: /* sysObjectID */ + objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t))); + break; + case 3: /* sysUpTime */ + { + snmp_get_sysuptime(value); + } + break; + case 4: /* sysContact */ + ocstrncpy(value,syscontact_ptr,len); + break; + case 5: /* sysName */ + ocstrncpy(value,sysname_ptr,len); + break; + case 6: /* sysLocation */ + ocstrncpy(value,syslocation_ptr,len); + break; + case 7: /* sysServices */ + { + s32_t *sint_ptr = value; + *sint_ptr = sysservices; + } + break; + }; +} + +static u8_t +system_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(value); + set_ok = 0; + id = od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + if ((syscontact_ptr != syscontact_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 5: /* sysName */ + if ((sysname_ptr != sysname_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 6: /* sysLocation */ + if ((syslocation_ptr != syslocation_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +system_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + id = od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + ocstrncpy(syscontact_ptr,value,len); + *syscontact_len_ptr = len; + break; + case 5: /* sysName */ + ocstrncpy(sysname_ptr,value,len); + *sysname_len_ptr = len; + break; + case 6: /* sysLocation */ + ocstrncpy(syslocation_ptr,value,len); + *syslocation_len_ptr = len; + break; + }; +} + +/** + * Returns interfaces.ifnumber object definition. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns interfaces.ifnumber object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +interfaces_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + if (od->id_inst_ptr[0] == 1) + { + s32_t *sint_ptr = value; + *sint_ptr = iflist_root.count; + } +} + +/** + * Returns ifentry object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id)); + switch (id) + { + case 1: /* ifIndex */ + case 3: /* ifType */ + case 4: /* ifMtu */ + case 8: /* ifOperStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ifDescr */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + /** @todo this should be some sort of sizeof(struct netif.name) */ + od->v_len = 2; + break; + case 5: /* ifSpeed */ + case 21: /* ifOutQLen */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + case 6: /* ifPhysAddress */ + { + struct netif *netif; + + snmp_ifindextonetif(ident[1], &netif); + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = netif->hwaddr_len; + } + break; + case 7: /* ifAdminStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ifLastChange */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 10: /* ifInOctets */ + case 11: /* ifInUcastPkts */ + case 12: /* ifInNUcastPkts */ + case 13: /* ifInDiscarts */ + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + case 16: /* ifOutOctets */ + case 17: /* ifOutUcastPkts */ + case 18: /* ifOutNUcastPkts */ + case 19: /* ifOutDiscarts */ + case 20: /* ifOutErrors */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 22: /* ifSpecific */ + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = ifspecific.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns ifentry object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +ifentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ifIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ifDescr */ + ocstrncpy(value,(u8_t*)netif->name,len); + break; + case 3: /* ifType */ + { + s32_t *sint_ptr = value; + *sint_ptr = netif->link_type; + } + break; + case 4: /* ifMtu */ + { + s32_t *sint_ptr = value; + *sint_ptr = netif->mtu; + } + break; + case 5: /* ifSpeed */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->link_speed; + } + break; + case 6: /* ifPhysAddress */ + ocstrncpy(value,netif->hwaddr,len); + break; + case 7: /* ifAdminStatus */ +#if LWIP_NETIF_LINK_CALLBACK + { + s32_t *sint_ptr = value; + if (netif_is_up(netif)) + { + if (netif_is_link_up(netif)) + { + *sint_ptr = 1; /* up */ + } + else + { + *sint_ptr = 7; /* lowerLayerDown */ + } + } + else + { + *sint_ptr = 2; /* down */ + } + } + break; +#endif + case 8: /* ifOperStatus */ + { + s32_t *sint_ptr = value; + if (netif_is_up(netif)) + { + *sint_ptr = 1; + } + else + { + *sint_ptr = 2; + } + } + break; + case 9: /* ifLastChange */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ts; + } + break; + case 10: /* ifInOctets */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinoctets; + } + break; + case 11: /* ifInUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinucastpkts; + } + break; + case 12: /* ifInNUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinnucastpkts; + } + break; + case 13: /* ifInDiscarts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifindiscards; + } + break; + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + /** @todo add these counters! */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 16: /* ifOutOctets */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutoctets; + } + break; + case 17: /* ifOutUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutucastpkts; + } + break; + case 18: /* ifOutNUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutnucastpkts; + } + break; + case 19: /* ifOutDiscarts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutdiscards; + } + break; + case 20: /* ifOutErrors */ + /** @todo add this counter! */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 21: /* ifOutQLen */ + /** @todo figure out if this must be 0 (no queue) or 1? */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 22: /* ifSpecific */ + objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t))); + break; + }; +} + +#if !SNMP_SAFE_REQUESTS +static u8_t +ifentry_set_test (struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id, set_ok; + + set_ok = 0; + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = value; + if (*sint_ptr == 1 || *sint_ptr == 2) + set_ok = 1; + } + break; + } + return set_ok; +} + +static void +ifentry_set_value (struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = value; + if (*sint_ptr == 1) + { + netif_set_up(netif); + } + else if (*sint_ptr == 2) + { + netif_set_down(netif); + } + } + break; + } +} +#endif /* SNMP_SAFE_REQUESTS */ + +/** + * Returns atentry object definitions. + * + * @param ident_len the address length (6) + * @param ident points to objectname.atifindex.atnetaddress + * @param od points to object definition. + */ +static void +atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* atIfIndex */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* atPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* atNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +atentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + struct ip_addr* ipaddr_ret; +#endif /* LWIP_ARP */ + struct ip_addr ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + ip.addr = htonl(ip.addr); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* atIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* atPhysAddress */ + { + struct eth_addr *dst = value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* atNetAddress */ + { + struct ip_addr *dst = value; + + *dst = *ipaddr_ret; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* ipForwarding */ + case 2: /* ipDefaultTTL */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 3: /* ipInReceives */ + case 4: /* ipInHdrErrors */ + case 5: /* ipInAddrErrors */ + case 6: /* ipForwDatagrams */ + case 7: /* ipInUnknownProtos */ + case 8: /* ipInDiscards */ + case 9: /* ipInDelivers */ + case 10: /* ipOutRequests */ + case 11: /* ipOutDiscards */ + case 12: /* ipOutNoRoutes */ + case 14: /* ipReasmReqds */ + case 15: /* ipReasmOKs */ + case 16: /* ipReasmFails */ + case 17: /* ipFragOKs */ + case 18: /* ipFragFails */ + case 19: /* ipFragCreates */ + case 23: /* ipRoutingDiscards */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 13: /* ipReasmTimeout */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ + { + s32_t *sint_ptr = value; +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + } + break; + case 2: /* ipDefaultTTL */ + { + s32_t *sint_ptr = value; + *sint_ptr = IP_DEFAULT_TTL; + } + break; + case 3: /* ipInReceives */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinreceives; + } + break; + case 4: /* ipInHdrErrors */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinhdrerrors; + } + break; + case 5: /* ipInAddrErrors */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinaddrerrors; + } + break; + case 6: /* ipForwDatagrams */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipforwdatagrams; + } + break; + case 7: /* ipInUnknownProtos */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinunknownprotos; + } + break; + case 8: /* ipInDiscards */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipindiscards; + } + break; + case 9: /* ipInDelivers */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipindelivers; + } + break; + case 10: /* ipOutRequests */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutrequests; + } + break; + case 11: /* ipOutDiscards */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutdiscards; + } + break; + case 12: /* ipOutNoRoutes */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutnoroutes; + } + break; + case 13: /* ipReasmTimeout */ + { + s32_t *sint_ptr = value; +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + } + break; + case 14: /* ipReasmReqds */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmreqds; + } + break; + case 15: /* ipReasmOKs */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmoks; + } + break; + case 16: /* ipReasmFails */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmfails; + } + break; + case 17: /* ipFragOKs */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragoks; + } + break; + case 18: /* ipFragFails */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragfails; + } + break; + case 19: /* ipFragCreates */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragcreates; + } + break; + case 23: /* ipRoutingDiscards */ + /** @todo can lwIP discard routes at all?? hardwire this to 0?? */ + { + u32_t *uint_ptr = value; + *uint_ptr = iproutingdiscards; + } + break; + }; +} + +/** + * Test ip object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static u8_t +ip_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + s32_t *sint_ptr = value; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + set_ok = 1; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + case 3: /* ipAdEntNetMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipAdEntIfIndex */ + case 4: /* ipAdEntBcastAddr */ + case 5: /* ipAdEntReasmMaxSize */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + u16_t ifidx; + struct ip_addr ip; + struct netif *netif = netif_list; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ip.addr = htonl(ip.addr); + ifidx = 0; + while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr)) + { + netif = netif->next; + ifidx++; + } + + if (netif != NULL) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + { + struct ip_addr *dst = value; + *dst = netif->ip_addr; + } + break; + case 2: /* ipAdEntIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = ifidx + 1; + } + break; + case 3: /* ipAdEntNetMask */ + { + struct ip_addr *dst = value; + *dst = netif->netmask; + } + break; + case 4: /* ipAdEntBcastAddr */ + { + s32_t *sint_ptr = value; + + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + *sint_ptr = ip_addr_broadcast.addr & 1; + } + break; + case 5: /* ipAdEntReasmMaxSize */ + { + s32_t *sint_ptr = value; +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + *sint_ptr = 0; +#endif + } + break; + } + } +} + +/** + * @note + * lwIP IP routing is currently using the network addresses in netif_list. + * if no suitable network IP is found in netif_list, the default_netif is used. + */ +static void +ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + case 7: /* ipRouteNextHop */ + case 11: /* ipRouteMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipRouteIfIndex */ + case 3: /* ipRouteMetric1 */ + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 8: /* ipRouteType */ + case 10: /* ipRouteAge */ + case 12: /* ipRouteMetric5 */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ipRouteProto */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 13: /* ipRouteInfo */ + /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = iprouteinfo.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + struct ip_addr dest; + s32_t *ident; + u8_t id; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &dest); + dest.addr = htonl(dest.addr); + + if (dest.addr == 0) + { + /* ip_route() uses default netif for default route */ + netif = netif_default; + } + else + { + /* not using ip_route(), need exact match! */ + netif = netif_list; + while ((netif != NULL) && + !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) ) + { + netif = netif->next; + } + } + if (netif != NULL) + { + id = ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte has 0.0.0.0 dest */ + dst->addr = 0; + } + else + { + /* netifs have netaddress dest */ + dst->addr = netif->ip_addr.addr & netif->netmask.addr; + } + } + break; + case 2: /* ipRouteIfIndex */ + { + s32_t *sint_ptr = value; + + snmp_netiftoifindex(netif, sint_ptr); + } + break; + case 3: /* ipRouteMetric1 */ + { + s32_t *sint_ptr = value; + + if (dest.addr == 0) + { + /* default rte has metric 1 */ + *sint_ptr = 1; + } + else + { + /* other rtes have metric 0 */ + *sint_ptr = 0; + } + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 12: /* ipRouteMetric5 */ + { + s32_t *sint_ptr = value; + /* not used */ + *sint_ptr = -1; + } + break; + case 7: /* ipRouteNextHop */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte: gateway */ + *dst = netif->gw; + } + else + { + /* other rtes: netif ip_addr */ + *dst = netif->ip_addr; + } + } + break; + case 8: /* ipRouteType */ + { + s32_t *sint_ptr = value; + + if (dest.addr == 0) + { + /* default rte is indirect */ + *sint_ptr = 4; + } + else + { + /* other rtes are direct */ + *sint_ptr = 3; + } + } + break; + case 9: /* ipRouteProto */ + { + s32_t *sint_ptr = value; + /* locally defined routes */ + *sint_ptr = 2; + } + break; + case 10: /* ipRouteAge */ + { + s32_t *sint_ptr = value; + /** @todo (sysuptime - timestamp last change) / 100 + @see snmp_insert_iprteidx_tree() */ + *sint_ptr = 0; + } + break; + case 11: /* ipRouteMask */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte use 0.0.0.0 mask */ + dst->addr = 0; + } + else + { + /* other rtes use netmask */ + *dst = netif->netmask; + } + } + break; + case 13: /* ipRouteInfo */ + objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t))); + break; + } + } +} + +static void +ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + case 4: /* ipNetToMediaType */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ipNetToMediaPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* ipNetToMediaNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + struct ip_addr* ipaddr_ret; +#endif /* LWIP_ARP */ + struct ip_addr ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + ip.addr = htonl(ip.addr); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ipNetToMediaPhysAddress */ + { + struct eth_addr *dst = value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* ipNetToMediaNetAddress */ + { + struct ip_addr *dst = value; + + *dst = *ipaddr_ret; + } + break; + case 4: /* ipNetToMediaType */ + { + s32_t *sint_ptr = value; + /* dynamic (?) */ + *sint_ptr = 3; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 27)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +icmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* icmpInMsgs */ + *uint_ptr = icmpinmsgs; + break; + case 2: /* icmpInErrors */ + *uint_ptr = icmpinerrors; + break; + case 3: /* icmpInDestUnreachs */ + *uint_ptr = icmpindestunreachs; + break; + case 4: /* icmpInTimeExcds */ + *uint_ptr = icmpintimeexcds; + break; + case 5: /* icmpInParmProbs */ + *uint_ptr = icmpinparmprobs; + break; + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = icmpinsrcquenchs; + break; + case 7: /* icmpInRedirects */ + *uint_ptr = icmpinredirects; + break; + case 8: /* icmpInEchos */ + *uint_ptr = icmpinechos; + break; + case 9: /* icmpInEchoReps */ + *uint_ptr = icmpinechoreps; + break; + case 10: /* icmpInTimestamps */ + *uint_ptr = icmpintimestamps; + break; + case 11: /* icmpInTimestampReps */ + *uint_ptr = icmpintimestampreps; + break; + case 12: /* icmpInAddrMasks */ + *uint_ptr = icmpinaddrmasks; + break; + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = icmpinaddrmaskreps; + break; + case 14: /* icmpOutMsgs */ + *uint_ptr = icmpoutmsgs; + break; + case 15: /* icmpOutErrors */ + *uint_ptr = icmpouterrors; + break; + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = icmpoutdestunreachs; + break; + case 17: /* icmpOutTimeExcds */ + *uint_ptr = icmpouttimeexcds; + break; + case 18: /* icmpOutParmProbs */ + *uint_ptr = icmpoutparmprobs; + break; + case 19: /* icmpOutSrcQuenchs */ + *uint_ptr = icmpoutsrcquenchs; + break; + case 20: /* icmpOutRedirects */ + *uint_ptr = icmpoutredirects; + break; + case 21: /* icmpOutEchos */ + *uint_ptr = icmpoutechos; + break; + case 22: /* icmpOutEchoReps */ + *uint_ptr = icmpoutechoreps; + break; + case 23: /* icmpOutTimestamps */ + *uint_ptr = icmpouttimestamps; + break; + case 24: /* icmpOutTimestampReps */ + *uint_ptr = icmpouttimestampreps; + break; + case 25: /* icmpOutAddrMasks */ + *uint_ptr = icmpoutaddrmasks; + break; + case 26: /* icmpOutAddrMaskReps */ + *uint_ptr = icmpoutaddrmaskreps; + break; + } +} + +#if LWIP_TCP +/** @todo tcp grp */ +static void +tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpRtoAlgorithm */ + case 2: /* tcpRtoMin */ + case 3: /* tcpRtoMax */ + case 4: /* tcpMaxConn */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 5: /* tcpActiveOpens */ + case 6: /* tcpPassiveOpens */ + case 7: /* tcpAttemptFails */ + case 8: /* tcpEstabResets */ + case 10: /* tcpInSegs */ + case 11: /* tcpOutSegs */ + case 12: /* tcpRetransSegs */ + case 14: /* tcpInErrs */ + case 15: /* tcpOutRsts */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 9: /* tcpCurrEstab */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + s32_t *sint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + break; + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + break; + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + break; + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + break; + case 5: /* tcpActiveOpens */ + *uint_ptr = tcpactiveopens; + break; + case 6: /* tcpPassiveOpens */ + *uint_ptr = tcppassiveopens; + break; + case 7: /* tcpAttemptFails */ + *uint_ptr = tcpattemptfails; + break; + case 8: /* tcpEstabResets */ + *uint_ptr = tcpestabresets; + break; + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) + { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) + { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + break; + case 10: /* tcpInSegs */ + *uint_ptr = tcpinsegs; + break; + case 11: /* tcpOutSegs */ + *uint_ptr = tcpoutsegs; + break; + case 12: /* tcpRetransSegs */ + *uint_ptr = tcpretranssegs; + break; + case 14: /* tcpInErrs */ + *uint_ptr = tcpinerrs; + break; + case 15: /* tcpOutRsts */ + *uint_ptr = tcpoutrsts; + break; + } +} +#ifdef THIS_SEEMS_UNUSED +static void +tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (10) */ + ident_len += 10; + ident -= 10; + + if (ident_len == 11) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpConnState */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* tcpConnLocalAddress */ + case 4: /* tcpConnRemAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 3: /* tcpConnLocalPort */ + case 5: /* tcpConnRemPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct ip_addr lip, rip; + u16_t lport, rport; + s32_t *ident; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &lip); + lip.addr = htonl(lip.addr); + lport = ident[5]; + snmp_oidtoip(&ident[6], &rip); + rip.addr = htonl(rip.addr); + rport = ident[10]; + + /** @todo find matching PCB */ +} +#endif /* if 0 */ +#endif + +static void +udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 6)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpInDatagrams */ + *uint_ptr = udpindatagrams; + break; + case 2: /* udpNoPorts */ + *uint_ptr = udpnoports; + break; + case 3: /* udpInErrors */ + *uint_ptr = udpinerrors; + break; + case 4: /* udpOutDatagrams */ + *uint_ptr = udpoutdatagrams; + break; + } +} + +static void +udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* udpLocalAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* udpLocalPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udpentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + struct udp_pcb *pcb; + struct ip_addr ip; + u16_t port; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ip.addr = htonl(ip.addr); + port = od->id_inst_ptr[5]; + + pcb = udp_pcbs; + while ((pcb != NULL) && + !((pcb->local_ip.addr == ip.addr) && + (pcb->local_port == port))) + { + pcb = pcb->next; + } + + if (pcb != NULL) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpLocalAddress */ + { + struct ip_addr *dst = value; + *dst = pcb->local_ip; + } + break; + case 2: /* udpLocalPort */ + { + s32_t *sint_ptr = value; + *sint_ptr = pcb->local_port; + } + break; + } + } +} + +static void +snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* snmpInPkts */ + case 2: /* snmpOutPkts */ + case 3: /* snmpInBadVersions */ + case 4: /* snmpInBadCommunityNames */ + case 5: /* snmpInBadCommunityUses */ + case 6: /* snmpInASNParseErrs */ + case 8: /* snmpInTooBigs */ + case 9: /* snmpInNoSuchNames */ + case 10: /* snmpInBadValues */ + case 11: /* snmpInReadOnlys */ + case 12: /* snmpInGenErrs */ + case 13: /* snmpInTotalReqVars */ + case 14: /* snmpInTotalSetVars */ + case 15: /* snmpInGetRequests */ + case 16: /* snmpInGetNexts */ + case 17: /* snmpInSetRequests */ + case 18: /* snmpInGetResponses */ + case 19: /* snmpInTraps */ + case 20: /* snmpOutTooBigs */ + case 21: /* snmpOutNoSuchNames */ + case 22: /* snmpOutBadValues */ + case 24: /* snmpOutGenErrs */ + case 25: /* snmpOutGetRequests */ + case 26: /* snmpOutGetNexts */ + case 27: /* snmpOutSetRequests */ + case 28: /* snmpOutGetResponses */ + case 29: /* snmpOutTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 30: /* snmpEnableAuthenTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +snmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* snmpInPkts */ + *uint_ptr = snmpinpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmpoutpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmpinbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmpinbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmpinbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmpinasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmpintoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmpinnosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmpinbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmpinreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmpingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmpintotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmpintotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmpingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmpingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmpinsetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmpingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmpintraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmpouttoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmpoutnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmpoutbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmpoutgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmpoutgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmpoutgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmpoutsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmpoutgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmpouttraps; + break; + case 30: /* snmpEnableAuthenTraps */ + *uint_ptr = *snmpenableauthentraps_ptr; + break; + }; +} + +/** + * Test snmp object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + */ +static u8_t +snmp_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + id = od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = value; + + if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default) + { + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == 1) || (*sint_ptr == 2)) + { + set_ok = 1; + } + } + else + { + /* const or hardwired value */ + if (*sint_ptr == snmpenableauthentraps_default) + { + set_ok = 1; + } + } + } + return set_ok; +} + +static void +snmp_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = value; + *snmpenableauthentraps_ptr = *sint_ptr; + } +} + +#endif /* LWIP_SNMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/snmp/mib_structs.c b/packages/net/lwip_tcpip/current/src/core/snmp/mib_structs.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/snmp/mib_structs.c @@ -0,0 +1,1183 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_structs.h" +#include "lwip/mem.h" + +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +/** node stack entry (old news?) */ +struct nse +{ + /** right child */ + struct mib_node* r_ptr; + /** right child identifier */ + s32_t r_id; + /** right child next level */ + u8_t r_nl; +}; +static u8_t node_stack_cnt; +static struct nse node_stack[NODE_STACK_SIZE]; + +/** + * Pushes nse struct onto stack. + */ +static void +push_node(struct nse* node) +{ + LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id)); + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = *node; + node_stack_cnt++; + } +} + +/** + * Pops nse struct from stack. + */ +static void +pop_node(struct nse* node) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + *node = node_stack[node_stack_cnt]; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id)); +} + +/** + * Conversion from ifIndex to lwIP netif + * @param ifindex is a s32_t object sub-identifier + * @param netif points to returned netif struct pointer + */ +void +snmp_ifindextonetif(s32_t ifindex, struct netif **netif) +{ + struct netif *nif = netif_list; + u16_t i, ifidx; + + ifidx = ifindex - 1; + i = 0; + while ((nif != NULL) && (i < ifidx)) + { + nif = nif->next; + i++; + } + *netif = nif; +} + +/** + * Conversion from lwIP netif to ifIndex + * @param netif points to a netif struct + * @param ifidx points to s32_t object sub-identifier + */ +void +snmp_netiftoifindex(struct netif *netif, s32_t *ifidx) +{ + struct netif *nif = netif_list; + u16_t i; + + i = 0; + while ((nif != NULL) && (nif != netif)) + { + nif = nif->next; + i++; + } + *ifidx = i+1; +} + +/** + * Conversion from oid to lwIP ip_addr + * @param ident points to s32_t ident[4] input + * @param ip points to output struct + */ +void +snmp_oidtoip(s32_t *ident, struct ip_addr *ip) +{ + u32_t ipa; + + ipa = ident[0]; + ipa <<= 8; + ipa |= ident[1]; + ipa <<= 8; + ipa |= ident[2]; + ipa <<= 8; + ipa |= ident[3]; + ip->addr = ipa; +} + +/** + * Conversion from lwIP ip_addr to oid + * @param ip points to input struct + * @param ident points to s32_t ident[4] output + */ +void +snmp_iptooid(struct ip_addr *ip, s32_t *ident) +{ + u32_t ipa; + + ipa = ip->addr; + ident[0] = (ipa >> 24) & 0xff; + ident[1] = (ipa >> 16) & 0xff; + ident[2] = (ipa >> 8) & 0xff; + ident[3] = ipa & 0xff; +} + +struct mib_list_node * +snmp_mib_ln_alloc(s32_t id) +{ + struct mib_list_node *ln; + + ln = (struct mib_list_node *)mem_malloc(sizeof(struct mib_list_node)); + if (ln != NULL) + { + ln->prev = NULL; + ln->next = NULL; + ln->objid = id; + ln->nptr = NULL; + } + return ln; +} + +void +snmp_mib_ln_free(struct mib_list_node *ln) +{ + mem_free(ln); +} + +struct mib_list_rootnode * +snmp_mib_lrn_alloc(void) +{ + struct mib_list_rootnode *lrn; + + lrn = (struct mib_list_rootnode*)mem_malloc(sizeof(struct mib_list_rootnode)); + if (lrn != NULL) + { + lrn->get_object_def = noleafs_get_object_def; + lrn->get_value = noleafs_get_value; + lrn->set_test = noleafs_set_test; + lrn->set_value = noleafs_set_value; + lrn->node_type = MIB_NODE_LR; + lrn->maxlength = 0; + lrn->head = NULL; + lrn->tail = NULL; + lrn->count = 0; + } + return lrn; +} + +void +snmp_mib_lrn_free(struct mib_list_rootnode *lrn) +{ + mem_free(lrn); +} + +/** + * Inserts node in idx list in a sorted + * (ascending order) fashion and + * allocates the node if needed. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param insn points to a pointer to the inserted node + * used for constructing the tree. + * @return -1 if failed, 1 if inserted, 2 if present. + */ +s8_t +snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn) +{ + struct mib_list_node *nn; + s8_t insert; + + LWIP_ASSERT("rn != NULL",rn != NULL); + + /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */ + insert = 0; + if (rn->head == NULL) + { + /* empty list, add first node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + rn->head = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + insert = -1; + } + } + else + { + struct mib_list_node *n; + /* at least one node is present */ + n = rn->head; + while ((n != NULL) && (insert == 0)) + { + if (n->objid == objid) + { + /* node is already there */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid)); + *insn = n; + insert = 2; + } + else if (n->objid < objid) + { + if (n->next == NULL) + { + /* alloc and insert at the tail */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + nn->next = NULL; + nn->prev = n; + n->next = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + else + { + /* there's more to explore: traverse list */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n")); + n = n->next; + } + } + else + { + /* n->objid > objid */ + /* alloc and insert between n->prev and n */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + if (n->prev == NULL) + { + /* insert at the head */ + nn->next = n; + nn->prev = NULL; + rn->head = nn; + n->prev = nn; + } + else + { + /* insert in the middle */ + nn->next = n; + nn->prev = n->prev; + n->prev->next = nn; + n->prev = nn; + } + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + } + } + if (insert == 1) + { + rn->count += 1; + } + LWIP_ASSERT("insert != 0",insert != 0); + return insert; +} + +/** + * Finds node in idx list and returns deletion mark. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param fn returns pointer to found node + * @return 0 if not found, 1 if deletable, + * 2 can't delete (2 or more children), 3 not a list_node + */ +s8_t +snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn) +{ + s8_t fc; + struct mib_list_node *n; + + LWIP_ASSERT("rn != NULL",rn != NULL); + n = rn->head; + while ((n != NULL) && (n->objid != objid)) + { + n = n->next; + } + if (n == NULL) + { + fc = 0; + } + else if (n->nptr == NULL) + { + /* leaf, can delete node */ + fc = 1; + } + else + { + struct mib_list_rootnode *r; + + if (n->nptr->node_type == MIB_NODE_LR) + { + r = (struct mib_list_rootnode *)n->nptr; + if (r->count > 1) + { + /* can't delete node */ + fc = 2; + } + else + { + /* count <= 1, can delete node */ + fc = 1; + } + } + else + { + /* other node type */ + fc = 3; + } + } + *fn = n; + return fc; +} + +/** + * Removes node from idx list + * if it has a single child left. + * + * @param rn points to the root node + * @param n points to the node to delete + * @return the nptr to be freed by caller + */ +struct mib_list_rootnode * +snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n) +{ + struct mib_list_rootnode *next; + + LWIP_ASSERT("rn != NULL",rn != NULL); + LWIP_ASSERT("n != NULL",n != NULL); + + /* caller must remove this sub-tree */ + next = (struct mib_list_rootnode*)(n->nptr); + rn->count -= 1; + + if (n == rn->head) + { + rn->head = n->next; + if (n->next != NULL) + { + /* not last node, new list begin */ + n->next->prev = NULL; + } + } + else if (n == rn->tail) + { + rn->tail = n->prev; + if (n->prev != NULL) + { + /* not last node, new list end */ + n->prev->next = NULL; + } + } + else + { + /* node must be in the middle */ + n->prev->next = n->next; + n->next->prev = n->prev; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid)); + snmp_mib_ln_free(n); + if (rn->count == 0) + { + rn->head = NULL; + rn->tail = NULL; + } + return next; +} + + + +/** + * Searches tree for the supplied (scalar?) object identifier. + * + * @param node points to the root of the tree ('.internet') + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param np points to the found object instance (rerurn) + * @return pointer to the requested parent (!) node if success, NULL otherwise + */ +struct mib_node * +snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np) +{ + u8_t node_type, ext_level; + + ext_level = 0; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident)); + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { + /* found it, if available proceed to child, otherwise inspect leaf */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + if (an->nptr[i] == NULL) + { + /* a scalar leaf OR table, + inspect remaining instance number / table index */ + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident++; + ident_len--; + node = an->nptr[i]; + } + } + else + { + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + if (ident_len > 0) + { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + /* found it, proceed to child */; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + if (ln->nptr == NULL) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)lrn; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = ln->nptr; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + u16_t i, len; + + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) + { + i++; + } + if (i < len) + { + s32_t debug_id; + + en->get_objid(en->addr_inf,ext_level,i,&debug_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident)); + if ((ext_level + 1) == en->tree_levels) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)en; + } + else + { + /* found it, proceed to child */ + ident_len--; + ident++; + ext_level++; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n")); + return NULL; + } + } + else if (node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + sn = (mib_scalar_node *)node; + if ((ident_len == 1) && (*ident == 0)) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)sn; + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n")); + return NULL; + } + } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test table for presence of at least one table entry. + */ +static u8_t +empty_table(struct mib_node *node) +{ + u8_t node_type; + u8_t empty = 0; + + if (node != NULL) + { + node_type = node->node_type; + if (node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + lrn = (struct mib_list_rootnode *)node; + if ((lrn->count == 0) || (lrn->head == NULL)) + { + empty = 1; + } + } + else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + an = (struct mib_array_node *)node; + if ((an->maxlength == 0) || (an->nptr == NULL)) + { + empty = 1; + } + } + else if (node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + en = (struct mib_external_node *)node; + if (en->tree_levels == 0) + { + empty = 1; + } + } + } + return empty; +} + +/** + * Tree expansion. + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type, ext_level, climb_tree; + + ext_level = 0; + /* reset node stack */ + node_stack_cnt = 0; + while (node != NULL) + { + climb_tree = 0; + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node (e.g. in a fixed size table) */ + if (an->objid[i] > *ident) + { + return (struct mib_node*)an; + } + else if ((i + 1) < an->maxlength) + { + /* an->objid[i] == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = an->objid[i + 1]; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* (i + 1) == an->maxlength */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + j = i + 1; + while ((j < an->maxlength) && (empty_table(an->nptr[j]))) + { + j++; + } + if (j < an->maxlength) + { + cur_node.r_ptr = an->nptr[j]; + cur_node.r_id = an->objid[j]; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (an->objid[i] == *ident) + { + ident_len--; + ident++; + } + else + { + /* an->objid[i] < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = an->nptr[i]; + } + } + else + { + /* i == an->maxlength */ + climb_tree = 1; + } + } + else + { + u8_t j; + /* ident_len == 0, complete with leftmost '.thing' */ + j = 0; + while ((j < an->maxlength) && empty_table(an->nptr[j])) + { + j++; + } + if (j < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j])); + oidret->id[oidret->len] = an->objid[j]; + (oidret->len)++; + if (an->nptr[j] == NULL) + { + /* leaf node */ + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[j]; + } + } + else + { + /* j == an->maxlength */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + if (ident_len > 0) + { + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid < *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + oidret->id[oidret->len] = ln->objid; + (oidret->len)++; + if (ln->nptr == NULL) + { + /* leaf node */ + if (ln->objid > *ident) + { + return (struct mib_node*)lrn; + } + else if (ln->next != NULL) + { + /* ln->objid == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = ln->next->objid; + (oidret->len)++; + return (struct mib_node*)lrn; + } + else + { + /* ln->next == NULL */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + struct nse cur_node; + + /* non-leaf, store right child ptr and id */ + jn = ln->next; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + cur_node.r_ptr = jn->nptr; + cur_node.r_id = jn->objid; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (ln->objid == *ident) + { + ident_len--; + ident++; + } + else + { + /* ln->objid < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = ln->nptr; + } + + } + else + { + /* ln == NULL */ + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + /* ident_len == 0, complete with leftmost '.thing' */ + jn = lrn->head; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid)); + oidret->id[oidret->len] = jn->objid; + (oidret->len)++; + if (jn->nptr == NULL) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n")); + return (struct mib_node*)lrn; + } + else + { + /* no leaf, continue */ + node = jn->nptr; + } + } + else + { + /* jn == NULL */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + s32_t ex_id; + + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + if (ident_len > 0) + { + u16_t i, len; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) + { + i++; + } + if (i < len) + { + /* add identifier to oidret */ + en->get_objid(en->addr_inf,ext_level,i,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + + if ((ext_level + 1) == en->tree_levels) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node */ + if (ex_id > *ident) + { + return (struct mib_node*)en; + } + else if ((i + 1) < len) + { + /* ex_id == *ident */ + en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id); + (oidret->len)--; + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + return (struct mib_node*)en; + } + else + { + /* (i + 1) == len */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + j = i + 1; + if (j < len) + { + /* right node is the current external node */ + cur_node.r_ptr = node; + en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id); + cur_node.r_nl = ext_level + 1; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) + { + ident_len--; + ident++; + } + else + { + /* external id < *ident */ + ident_len = 0; + } + /* proceed to child */ + ext_level++; + } + } + else + { + /* i == len (en->level_len()) */ + climb_tree = 1; + } + } + else + { + /* ident_len == 0, complete with leftmost '.thing' */ + en->get_objid(en->addr_inf,ext_level,0,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + if ((ext_level + 1) == en->tree_levels) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n")); + return (struct mib_node*)en; + } + else + { + /* no leaf, proceed to child */ + ext_level++; + } + } + } + else if(node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + /* scalar node */ + sn = (mib_scalar_node *)node; + if (ident_len > 0) + { + /* at .0 */ + climb_tree = 1; + } + else + { + /* ident_len == 0, complete object identifier */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n")); + return (struct mib_node*)sn; + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + + if (climb_tree) + { + struct nse child; + + /* find right child ptr */ + child.r_ptr = NULL; + child.r_id = 0; + child.r_nl = 0; + while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) + { + pop_node(&child); + /* trim returned oid */ + (oidret->len)--; + } + if (child.r_ptr != NULL) + { + /* incoming ident is useless beyond this point */ + ident_len = 0; + oidret->id[oidret->len] = child.r_id; + oidret->len++; + node = child.r_ptr; + ext_level = child.r_nl; + } + else + { + /* tree ends here ... */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n")); + return NULL; + } + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test object identifier for the iso.org.dod.internet prefix. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @return 1 if it matches, 0 otherwise + */ +u8_t +snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) +{ + if ((ident_len > 3) && + (ident[0] == 1) && (ident[1] == 3) && + (ident[2] == 6) && (ident[3] == 1)) + { + return 1; + } + else + { + return 0; + } +} + +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + +#endif /* LWIP_SNMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/snmp/msg_in.c b/packages/net/lwip_tcpip/current/src/core/snmp/msg_in.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/snmp/msg_in.c @@ -0,0 +1,1454 @@ +/** + * @file + * SNMP input message processing (RFC1157). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" +#include "lwip/snmp_structs.h" + +#include + +/* public (non-static) constants */ +/** SNMP v1 == 0 */ +const s32_t snmp_version = 0; +/** default SNMP community string */ +const char snmp_publiccommunity[7] = "public"; + +/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */ +struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS]; +/* UDP Protocol Control Block */ +struct udp_pcb *snmp1_pcb; + +static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); +static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); + + +/** + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161. + */ +void +snmp_init(void) +{ + struct snmp_msg_pstat *msg_ps; + u8_t i; + + snmp1_pcb = udp_new(); + if (snmp1_pcb != NULL) + { + udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT); + udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT); + } + msg_ps = &msg_input_list[0]; + for (i=0; istate = SNMP_MSG_EMPTY; + msg_ps->error_index = 0; + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps++; + } + trap_msg.pcb = snmp1_pcb; + /* The coldstart trap will only be output + if our outgoing interface is up & configured */ + snmp_coldstart_trap(); +} + +static void +snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) +{ + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + msg_ps->error_status = error; + msg_ps->error_index = 1 + msg_ps->vb_idx; + snmp_send_response(msg_ps); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +static void +snmp_ok_response(struct snmp_msg_pstat *msg_ps) +{ + err_t err_ret; + + err_ret = snmp_send_response(msg_ps); + if (err_ret == ERR_MEM) + { + /* serious memory problem, can't return tooBig */ + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status)); + } + /* free varbinds (if available) */ + snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = msg_ps->ext_object_def.asn_type; + vb->value_len = msg_ps->ext_object_def.v_len; + if (vb->value_len > 0) + { + vb->value = mem_malloc(vb->value_len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + mem_free(vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + /* allocate output varbind */ + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = object_def.asn_type; + vb->value_len = object_def.v_len; + if (vb->value_len > 0) + { + vb->value = mem_malloc(vb->value_len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value != NULL) + { + mn->get_value(&object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + mem_free(vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP GETNEXT. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + vb = snmp_varbind_alloc(&msg_ps->ext_oid, + msg_ps->ext_object_def.asn_type, + msg_ps->ext_object_def.v_len); + if (vb != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_obj_id oid; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) + { + if (msg_ps->vb_ptr->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } + } + else + { + mn = NULL; + } + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_oid = oid; + + en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]); + } + else + { + /* internal object */ + struct obj_def object_def; + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(1, &oid.id[oid.len - 1], &object_def); + + vb = snmp_varbind_alloc(&oid, object_def.asn_type, object_def.v_len); + if (vb != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST; + en->set_test_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST) + { + struct mib_external_node *en; + + /* set_test() answer*/ + en = msg_ps->ext_mib_node; + + if (msg_ps->ext_object_def.access == MIB_OBJECT_READ_WRITE) + { + if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) && + (en->set_test_a(request_id,&msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE; + en->set_value_q(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* set_value failed, object has disappeared for some odd reason?? */ + snmp_error_response(msg_ps,SNMP_ES_GENERROR); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE) + { + struct mib_external_node *en; + + /** set_value_a() */ + en = msg_ps->ext_mib_node; + en->set_value_a(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value); + + /** @todo use set_value_pc() if toobig */ + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + msg_ps->vb_idx += 1; + } + + /* test all values before setting */ + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST; + + if (object_def.access == MIB_OBJECT_READ_WRITE) + { + if ((object_def.asn_type == msg_ps->vb_ptr->value_type) && + (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + msg_ps->vb_idx = 0; + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + } + + /* set all values "atomically" (be as "atomic" as possible) */ + while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /* skip iso prefix test, was done previously while settesting() */ + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + /* check if object is still available + (e.g. external hot-plug thingy present?) */ + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S; + mn->get_object_def(np.ident_len, np.ident, &object_def); + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + msg_ps->vb_idx += 1; + } + } + } + if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + /* simply echo the input if we can set it + @todo do we need to return the actual value? + e.g. if value is silently modified or behaves sticky? */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + snmp_ok_response(msg_ps); + } +} + + +/** + * Handle one internal or external event. + * Called for one async event. (recv external/private answer) + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + */ +void +snmp_msg_event(u8_t request_id) +{ + struct snmp_msg_pstat *msg_ps; + + if (request_id < SNMP_CONCURRENT_REQUESTS) + { + msg_ps = &msg_input_list[request_id]; + if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + snmp_msg_getnext_event(request_id, msg_ps); + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) + { + snmp_msg_get_event(request_id, msg_ps); + } + else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_msg_set_event(request_id, msg_ps); + } + } +} + + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + struct udp_hdr *udphdr; + + /* suppress unused argument warning */ + LWIP_UNUSED_ARG(arg); + /* peek in the UDP header (goto IP payload) */ + if(pbuf_header(p, UDP_HLEN)){ + LWIP_ASSERT("Can't move to UDP header", 0); + pbuf_free(p); + return; + } + udphdr = p->payload; + + /* check if datagram is really directed at us (including broadcast requests) */ + if ((pcb == snmp1_pcb) && (ntohs(udphdr->dest) == SNMP_IN_PORT)) + { + struct snmp_msg_pstat *msg_ps; + u8_t req_idx; + + /* traverse input message process list, look for SNMP_MSG_EMPTY */ + msg_ps = &msg_input_list[0]; + req_idx = 0; + while ((req_idxstate != SNMP_MSG_EMPTY)) + { + req_idx++; + msg_ps++; + } + if (req_idx != SNMP_CONCURRENT_REQUESTS) + { + err_t err_ret; + u16_t payload_len; + u16_t payload_ofs; + u16_t varbind_ofs = 0; + + /* accepting request */ + snmp_inc_snmpinpkts(); + /* record used 'protocol control block' */ + msg_ps->pcb = pcb; + /* source address (network order) */ + msg_ps->sip = *addr; + /* source port (host order (lwIP oddity)) */ + msg_ps->sp = port; + /* read UDP payload length from UDP header */ + payload_len = ntohs(udphdr->len) - UDP_HLEN; + + /* adjust to UDP payload */ + payload_ofs = UDP_HLEN; + + /* check total length, version, community, pdu type */ + err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); + if (((msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) || + (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) || + (msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)) && + ((msg_ps->error_status == SNMP_ES_NOERROR) && + (msg_ps->error_index == 0)) ) + { + /* Only accept requests and requests without error (be robust) */ + err_ret = err_ret; + } + else + { + /* Reject response and trap headers or error requests as input! */ + err_ret = ERR_ARG; + } + if (err_ret == ERR_OK) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); + + /* Builds a list of variable bindings. Copy the varbinds from the pbuf + chain to glue them when these are divided over two or more pbuf's. */ + err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); + if ((err_ret == ERR_OK) && (msg_ps->invb.count > 0)) + { + /* we've decoded the incoming message, release input msg now */ + pbuf_free(p); + + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps->error_index = 0; + /* find object for each variable binding */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + /* first variable binding from list to inspect */ + msg_ps->vb_idx = 0; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); + + /* handle input event and as much objects as possible in one go */ + snmp_msg_event(req_idx); + } + else + { + /* varbind-list decode failed, or varbind list empty. + drop request silently, do not return error! + (errors are only returned for a specific varbind failure) */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); + } + } + else + { + /* header check failed + drop request silently, do not return error! */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); + } + } + else + { + /* exceeding number of concurrent requests */ + pbuf_free(p); + } + } + else + { + /* datagram not for us */ + pbuf_free(p); + } +} + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param p points to pbuf chain of SNMP message (UDP payload) + * @param ofs points to first octet of SNMP message + * @param pdu_len the length of the UDP payload + * @param ofs_ret returns the ofset of the variable bindings + * @param m_stat points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_ARG SNMP header is either malformed or rejected + */ +static err_t +snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, ofs_base; + u8_t len_octets; + u8_t type; + s32_t version; + + ofs_base = ofs; + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (pdu_len != (1 + len_octets + len)) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (version) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + if (version != 0) + { + /* not version 1 */ + snmp_inc_snmpinbadversions(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) + { + /* can't decode or no octet string (community) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* add zero terminator */ + len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN)); + m_stat->community[len] = 0; + m_stat->com_strlen = len; + if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) + { + /** @todo: move this if we need to check more names */ + snmp_inc_snmpinbadcommunitynames(); + snmp_authfail_trap(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch(type) + { + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_inc_snmpingetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_inc_snmpingetnexts(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP): + /* GetResponse PDU */ + snmp_inc_snmpingetresponses(); + derr = ERR_ARG; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_inc_snmpinsetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP): + /* Trap PDU */ + snmp_inc_snmpintraps(); + derr = ERR_ARG; + break; + default: + snmp_inc_snmpinasnparseerrs(); + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + /* unsupported input PDU for this agent (no parse error) */ + return ERR_ARG; + } + m_stat->rt = type & 0x1F; + ofs += (1 + len_octets); + if (len != (pdu_len - (ofs - ofs_base))) + { + /* decoded PDU length does not equal actual payload length */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (request ID) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-status) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be noError (0) for incoming requests. + log errors for mib-2 completeness and for debug purposes */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpintoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpinnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpinbadvalues(); + break; + case SNMP_ES_READONLY: + snmp_inc_snmpinreadonlys(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpingenerrs(); + break; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-index) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be 0 for incoming requests. + decode anyway to catch bad integers (and dirty tricks) */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + *ofs_ret = ofs; + return ERR_OK; +} + +static err_t +snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, vb_len; + u8_t len_octets; + u8_t type; + + /* variable binding list */ + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + + /* start with empty list */ + m_stat->invb.count = 0; + m_stat->invb.head = NULL; + m_stat->invb.tail = NULL; + + while (vb_len > 0) + { + struct snmp_obj_id oid, oid_value; + struct snmp_varbind *vb; + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) || + (len == 0) || (len > vb_len)) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets); + vb_len -= (1 + len_octets); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) + { + /* can't decode object name length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid); + if (derr != ERR_OK) + { + /* can't decode object name */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + /* can't decode object value length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + + switch (type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t)); + if (vb != NULL) + { + s32_t *vptr = vb->value; + + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t)); + if (vb != NULL) + { + u32_t *vptr = vb->value; + + derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb = snmp_varbind_alloc(&oid, type, len); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + vb = snmp_varbind_alloc(&oid, type, 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value); + if (derr == ERR_OK) + { + vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t)); + if (vb != NULL) + { + u8_t i = oid_value.len; + s32_t *vptr = vb->value; + + while(i > 0) + { + i--; + vptr[i] = oid_value.id[i]; + } + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + if (len == 4) + { + /* must be exactly 4 octets! */ + vb = snmp_varbind_alloc(&oid, type, 4); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + } + else + { + derr = ERR_ARG; + } + break; + default: + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + } + + if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_add_snmpintotalsetvars(m_stat->invb.count); + } + else + { + snmp_add_snmpintotalreqvars(m_stat->invb.count); + } + + *ofs_ret = ofs; + return ERR_OK; +} + +struct snmp_varbind* +snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) +{ + struct snmp_varbind *vb; + + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + u8_t i; + + vb->next = NULL; + vb->prev = NULL; + i = oid->len; + vb->ident_len = i; + if (i > 0) + { + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i); + LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL); + if (vb->ident == NULL) + { + mem_free(vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } + } + else + { + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; + } + vb->value_type = type; + vb->value_len = len; + if (len > 0) + { + /* allocate raw bytes for our object value */ + vb->value = mem_malloc(len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value == NULL) + { + if (vb->ident != NULL) + { + mem_free(vb->ident); + } + mem_free(vb); + return NULL; + } + } + else + { + /* ASN1_NUL type, or zero length ASN1_OC_STR */ + vb->value = NULL; + } + } + return vb; +} + +void +snmp_varbind_free(struct snmp_varbind *vb) +{ + if (vb->value != NULL ) + { + mem_free(vb->value); + } + if (vb->ident != NULL ) + { + mem_free(vb->ident); + } + mem_free(vb); +} + +void +snmp_varbind_list_free(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb, *prev; + + vb = root->tail; + while ( vb != NULL ) + { + prev = vb->prev; + snmp_varbind_free(vb); + vb = prev; + } + root->count = 0; + root->head = NULL; + root->tail = NULL; +} + +void +snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb) +{ + if (root->count == 0) + { + /* add first varbind to list */ + root->head = vb; + root->tail = vb; + } + else + { + /* add nth varbind to list tail */ + root->tail->next = vb; + vb->prev = root->tail; + root->tail = vb; + } + root->count += 1; +} + +struct snmp_varbind* +snmp_varbind_tail_remove(struct snmp_varbind_root *root) +{ + struct snmp_varbind* vb; + + if (root->count > 0) + { + /* remove tail varbind */ + vb = root->tail; + root->tail = vb->prev; + vb->prev->next = NULL; + root->count -= 1; + } + else + { + /* nothing to remove */ + vb = NULL; + } + return vb; +} + +#endif /* LWIP_SNMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/snmp/msg_out.c b/packages/net/lwip_tcpip/current/src/core/snmp/msg_out.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/core/snmp/msg_out.c @@ -0,0 +1,683 @@ +/** + * @file + * SNMP output message processing (RFC1157). + * + * Output responses and traps are build in two passes: + * + * Pass 0: iterate over the output message backwards to determine encoding lengths + * Pass 1: the actual forward encoding of internal form into ASN1 + * + * The single-pass encoding method described by Comer & Stevens + * requires extra buffer space and copying for reversal of the packet. + * The buffer requirement can be prohibitively large for big payloads + * (>= 484) therefore we use the two encoding passes. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + struct ip_addr dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +/** TRAP message structure */ +struct snmp_msg_trap trap_msg; + +static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len); +static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root); + +static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p); +static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p); +static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs); + +/** + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, struct ip_addr *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].dip.addr = htonl(dst->addr); + } +} + +/** + * Sends a 'getresponse' message to the request originator. + * + * @param m_stat points to the current message request state source + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the m_stat + * and provide error-status and index (except for tooBig errors) ... + */ +err_t +snmp_send_response(struct snmp_msg_pstat *m_stat) +{ + struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0}; + struct pbuf *p; + u16_t tot_len; + err_t err; + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&m_stat->outvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + + /* try allocating pbuf(s) for complete response */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n")); + + /* can't construct reply, return error-status tooBig */ + m_stat->error_status = SNMP_ES_TOOBIG; + m_stat->error_index = 0; + /* pass 0, recalculate lengths, for empty varbind-list */ + tot_len = snmp_varbind_list_sum(&emptyvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + /* retry allocation once for header and empty varbind-list */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + } + if (p != NULL) + { + /* first pbuf alloc try or retry alloc success */ + u16_t ofs; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n")); + + /* pass 1, size error, encode packet ino the pbuf(s) */ + ofs = snmp_resp_header_enc(m_stat, p); + if (m_stat->error_status == SNMP_ES_TOOBIG) + { + snmp_varbind_list_enc(&emptyvb, p, ofs); + } + else + { + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); + } + + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpouttoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpoutnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpoutbadvalues(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpoutgenerrs(); + break; + } + snmp_inc_snmpoutgetresponses(); + snmp_inc_snmpoutpkts(); + + /** @todo do we need separate rx and tx pcbs for threaded case? */ + /** connect to the originating source */ + udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp); + err = udp_send(m_stat->pcb, p); + if (err == ERR_MEM) + { + /** @todo release some memory, retry and return tooBig? tooMuchHassle? */ + err = ERR_MEM; + } + else + { + err = ERR_OK; + } + /** disassociate remote address and port with this pcb */ + udp_disconnect(m_stat->pcb); + + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n")); + return err; + } + else + { + /* first pbuf alloc try or retry alloc failed + very low on memory, couldn't return tooBig */ + return ERR_MEM; + } +} + + +/** + * Sends an generic or enterprise specific trap message. + * + * @param generic_trap is the trap code + * @param eoid points to enterprise object identifier + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the trap_msg + * @note the use of the enterpise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) +{ + struct snmp_trap_dst *td; + struct netif *dst_if; + struct ip_addr dst_ip; + struct pbuf *p; + u16_t i,tot_len; + + for (i=0, td = &trap_dst[0]; ienable != 0) && (td->dip.addr != 0)) + { + /* network order trap destination */ + trap_msg.dip.addr = td->dip.addr; + /* lookup current source address for this dst */ + dst_if = ip_route(&td->dip); + dst_ip.addr = ntohl(dst_if->ip_addr.addr); + trap_msg.sip_raw[0] = dst_ip.addr >> 24; + trap_msg.sip_raw[1] = dst_ip.addr >> 16; + trap_msg.sip_raw[2] = dst_ip.addr >> 8; + trap_msg.sip_raw[3] = dst_ip.addr; + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** connect to the TRAP destination */ + udp_connect(trap_msg.pcb, &trap_msg.dip, SNMP_TRAP_PORT); + udp_send(trap_msg.pcb, p); + /** disassociate remote address and port with this pcb */ + udp_disconnect(trap_msg.pcb); + + pbuf_free(p); + } + else + { + return ERR_MEM; + } + } + } + return ERR_OK; +} + +void +snmp_coldstart_trap(void) +{ + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0); +} + +void +snmp_authfail_trap(void) +{ + u8_t enable; + snmp_get_snmpenableauthentraps(&enable); + if (enable == 1) + { + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0); + } +} + +/** + * Sums response header field lengths from tail to head and + * returns resp_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param rhl points to returned header lengths + * @return the required lenght for encoding the response header + */ +static u16_t +snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_resp_header_lengths *rhl; + + rhl = &m_stat->rhl; + tot_len = vb_len; + snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen); + snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen); + tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen; + + snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen); + snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen); + tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen; + + snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen); + snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen); + tot_len += 1 + rhl->ridlenlen + rhl->ridlen; + + rhl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen); + tot_len += 1 + rhl->pdulenlen; + + rhl->comlen = m_stat->com_strlen; + snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen); + tot_len += 1 + rhl->comlenlen + rhl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen); + snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen); + tot_len += 1 + rhl->verlen + rhl->verlenlen; + + rhl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen); + tot_len += 1 + rhl->seqlenlen; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param thl points to returned header lengths + * @return the required lenght for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_trap_header_lengths *thl; + + thl = &m_trap->thl; + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen); + snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen); + tot_len += 1 + thl->tslen + thl->tslenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen); + snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen); + tot_len += 1 + thl->strplen + thl->strplenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen); + snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen); + tot_len += 1 + thl->gtrplen + thl->gtrplenlen; + + thl->aaddrlen = 4; + snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen); + tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen; + + snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen); + snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen); + tot_len += 1 + thl->eidlen + thl->eidlenlen; + + thl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen); + tot_len += 1 + thl->pdulenlen; + + thl->comlen = sizeof(snmp_publiccommunity) - 1; + snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen); + tot_len += 1 + thl->comlenlen + thl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen); + snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen); + tot_len += 1 + thl->verlen + thl->verlenlen; + + thl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen); + tot_len += 1 + thl->seqlenlen; + + return tot_len; +} + +/** + * Sums varbind lengths from tail to head and + * annotates lengths in varbind for second encoding pass. + * + * @param root points to the root of the variable binding list + * @return the required lenght for encoding the variable bindings + */ +static u16_t +snmp_varbind_list_sum(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb; + u32_t *uint_ptr; + s32_t *sint_ptr; + u16_t tot_len; + + tot_len = 0; + vb = root->tail; + while ( vb != NULL ) + { + /* encoded value lenght depends on type */ + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = vb->value; + snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = vb->value; + snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb->vlen = vb->value_len; + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = vb->value; + snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen); + break; + default: + /* unsupported type */ + vb->vlen = 0; + break; + }; + /* encoding length of value length field */ + snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen); + snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen); + snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen); + + vb->seqlen = 1 + vb->vlenlen + vb->vlen; + vb->seqlen += 1 + vb->olenlen + vb->olen; + snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen); + + /* varbind seq */ + tot_len += 1 + vb->seqlenlen + vb->seqlen; + + vb = vb->prev; + } + + /* varbind-list seq */ + root->seqlen = tot_len; + snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen); + tot_len += 1 + root->seqlenlen; + + return tot_len; +} + +/** + * Encodes response header from head to tail. + */ +static u16_t +snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen); + ofs += m_stat->rhl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen); + ofs += m_stat->rhl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version); + ofs += m_stat->rhl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen); + ofs += m_stat->rhl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community); + ofs += m_stat->rhl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen); + ofs += m_stat->rhl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen); + ofs += m_stat->rhl.ridlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid); + ofs += m_stat->rhl.ridlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen); + ofs += m_stat->rhl.errstatlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status); + ofs += m_stat->rhl.errstatlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen); + ofs += m_stat->rhl.erridxlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index); + ofs += m_stat->rhl.erridxlen; + + return ofs; +} + +/** + * Encodes trap header from head to tail. + */ +static u16_t +snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen); + ofs += m_trap->thl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen); + ofs += m_trap->thl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version); + ofs += m_trap->thl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen); + ofs += m_trap->thl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]); + ofs += m_trap->thl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen); + ofs += m_trap->thl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen); + ofs += m_trap->thl.eidlenlen; + snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]); + ofs += m_trap->thl.eidlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen); + ofs += m_trap->thl.aaddrlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]); + ofs += m_trap->thl.aaddrlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen); + ofs += m_trap->thl.gtrplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap); + ofs += m_trap->thl.gtrplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen); + ofs += m_trap->thl.strplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap); + ofs += m_trap->thl.strplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen); + ofs += m_trap->thl.tslenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts); + ofs += m_trap->thl.tslen; + + return ofs; +} + +/** + * Encodes varbind list from head to tail. + */ +static u16_t +snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs) +{ + struct snmp_varbind *vb; + s32_t *sint_ptr; + u32_t *uint_ptr; + u8_t *raw_ptr; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, root->seqlen); + ofs += root->seqlenlen; + + vb = root->head; + while ( vb != NULL ) + { + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->seqlen); + ofs += vb->seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->olen); + ofs += vb->olenlen; + snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]); + ofs += vb->olen; + + snmp_asn1_enc_type(p, ofs, vb->value_type); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->vlen); + ofs += vb->vlenlen; + + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = vb->value; + snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = vb->value; + snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + raw_ptr = vb->value; + snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = vb->value; + snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr); + break; + default: + /* unsupported type */ + break; + }; + ofs += vb->vlen; + vb = vb->next; + } + return ofs; +} + +#endif /* LWIP_SNMP */ diff --git a/packages/net/lwip_tcpip/current/src/core/stats.c b/packages/net/lwip_tcpip/current/src/core/stats.c --- a/packages/net/lwip_tcpip/current/src/core/stats.c +++ b/packages/net/lwip_tcpip/current/src/core/stats.c @@ -1,3 +1,9 @@ +/** + * @file + * Statistics module + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -30,86 +36,114 @@ * */ -#include - #include "lwip/opt.h" +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + #include "lwip/def.h" - #include "lwip/stats.h" #include "lwip/mem.h" +#include -#if LWIP_STATS struct stats_ lwip_stats; -void -stats_init(void) -{ - memset(&lwip_stats, 0, sizeof(struct stats_)); -} #if LWIP_STATS_DISPLAY void stats_display_proto(struct stats_proto *proto, char *name) { LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); - LWIP_PLATFORM_DIAG(("xmit: %"S16_F"\n\t", proto->xmit)); - LWIP_PLATFORM_DIAG(("rexmit: %"S16_F"\n\t", proto->rexmit)); - LWIP_PLATFORM_DIAG(("recv: %"S16_F"\n\t", proto->recv)); - LWIP_PLATFORM_DIAG(("fw: %"S16_F"\n\t", proto->fw)); - LWIP_PLATFORM_DIAG(("drop: %"S16_F"\n\t", proto->drop)); - LWIP_PLATFORM_DIAG(("chkerr: %"S16_F"\n\t", proto->chkerr)); - LWIP_PLATFORM_DIAG(("lenerr: %"S16_F"\n\t", proto->lenerr)); - LWIP_PLATFORM_DIAG(("memerr: %"S16_F"\n\t", proto->memerr)); - LWIP_PLATFORM_DIAG(("rterr: %"S16_F"\n\t", proto->rterr)); - LWIP_PLATFORM_DIAG(("proterr: %"S16_F"\n\t", proto->proterr)); - LWIP_PLATFORM_DIAG(("opterr: %"S16_F"\n\t", proto->opterr)); - LWIP_PLATFORM_DIAG(("err: %"S16_F"\n\t", proto->err)); - LWIP_PLATFORM_DIAG(("cachehit: %"S16_F"\n", proto->cachehit)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); } +#if IGMP_STATS void -stats_display_pbuf(struct stats_pbuf *pbuf) +stats_display_igmp(struct stats_igmp *igmp) { - LWIP_PLATFORM_DIAG(("\nPBUF\n\t")); - LWIP_PLATFORM_DIAG(("avail: %"S16_F"\n\t", pbuf->avail)); - LWIP_PLATFORM_DIAG(("used: %"S16_F"\n\t", pbuf->used)); - LWIP_PLATFORM_DIAG(("max: %"S16_F"\n\t", pbuf->max)); - LWIP_PLATFORM_DIAG(("err: %"S16_F"\n\t", pbuf->err)); - LWIP_PLATFORM_DIAG(("alloc_locked: %"S16_F"\n\t", pbuf->alloc_locked)); - LWIP_PLATFORM_DIAG(("refresh_locked: %"S16_F"\n", pbuf->refresh_locked)); + LWIP_PLATFORM_DIAG(("\nIGMP\n\t")); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("v1_rxed: %"STAT_COUNTER_F"\n\t", igmp->v1_rxed)); + LWIP_PLATFORM_DIAG(("join_sent: %"STAT_COUNTER_F"\n\t", igmp->join_sent)); + LWIP_PLATFORM_DIAG(("leave_sent: %"STAT_COUNTER_F"\n\t", igmp->leave_sent)); + LWIP_PLATFORM_DIAG(("unicast_query: %"STAT_COUNTER_F"\n\t", igmp->unicast_query)); + LWIP_PLATFORM_DIAG(("report_sent: %"STAT_COUNTER_F"\n\t", igmp->report_sent)); + LWIP_PLATFORM_DIAG(("report_rxed: %"STAT_COUNTER_F"\n\t", igmp->report_rxed)); + LWIP_PLATFORM_DIAG(("group_query_rxed: %"STAT_COUNTER_F"\n", igmp->group_query_rxed)); } +#endif /* IGMP_STATS */ +#if MEM_STATS || MEMP_STATS void stats_display_mem(struct stats_mem *mem, char *name) { - LWIP_PLATFORM_DIAG(("\n MEM %s\n\t", name)); - LWIP_PLATFORM_DIAG(("avail: %"S16_F"\n\t", mem->avail)); - LWIP_PLATFORM_DIAG(("used: %"S16_F"\n\t", mem->used)); - LWIP_PLATFORM_DIAG(("max: %"S16_F"\n\t", mem->max)); - LWIP_PLATFORM_DIAG(("err: %"S16_F"\n", mem->err)); - + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); } +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + if(index < MEMP_MAX) { + stats_display_mem(mem, memp_names[index]); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + void stats_display(void) { s16_t i; - char * memp_names[] = {"PBUF", "RAW_PCB", "UDP_PCB", "TCP_PCB", "TCP_PCB_LISTEN", - "TCP_SEG", "NETBUF", "NETCONN", "API_MSG", "TCP_MSG", "TIMEOUT"}; - stats_display_proto(&lwip_stats.link, "LINK"); - stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG"); - stats_display_proto(&lwip_stats.ip, "IP"); - stats_display_proto(&lwip_stats.icmp, "ICMP"); - stats_display_proto(&lwip_stats.udp, "UDP"); - stats_display_proto(&lwip_stats.tcp, "TCP"); - stats_display_pbuf(&lwip_stats.pbuf); - stats_display_mem(&lwip_stats.mem, "HEAP"); + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); for (i = 0; i < MEMP_MAX; i++) { - stats_display_mem(&lwip_stats.memp[i], memp_names[i]); + MEMP_STATS_DISPLAY(i); } - + SYS_STATS_DISPLAY(); } #endif /* LWIP_STATS_DISPLAY */ + #endif /* LWIP_STATS */ diff --git a/packages/net/lwip_tcpip/current/src/core/sys.c b/packages/net/lwip_tcpip/current/src/core/sys.c --- a/packages/net/lwip_tcpip/current/src/core/sys.c +++ b/packages/net/lwip_tcpip/current/src/core/sys.c @@ -1,3 +1,9 @@ +/** + * @file + * lwIP Operating System abstraction + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -30,85 +36,101 @@ * */ +#include "lwip/opt.h" + +#if (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */ + #include "lwip/sys.h" -#include "lwip/opt.h" #include "lwip/def.h" #include "lwip/memp.h" +#include "lwip/tcpip.h" -#if (NO_SYS == 0) - +/** + * Struct used for sys_sem_wait_timeout() to tell wether the time + * has run out or the semaphore has really become available. + */ struct sswt_cb { - s16_t timeflag; - sys_sem_t *psem; + s16_t timeflag; + sys_sem_t *psem; }; - - +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts (for this thread) are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ void sys_mbox_fetch(sys_mbox_t mbox, void **msg) { - u32_t time; + u32_t time_needed; struct sys_timeouts *timeouts; - struct sys_timeout *tmptimeout; + struct sys_timeo *tmptimeout; sys_timeout_handler h; void *arg; - again: timeouts = sys_arch_timeouts(); if (!timeouts || !timeouts->next) { - sys_arch_mbox_fetch(mbox, msg, 0); + UNLOCK_TCPIP_CORE(); + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + LOCK_TCPIP_CORE(); } else { if (timeouts->next->time > 0) { - time = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); + UNLOCK_TCPIP_CORE(); + time_needed = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); + LOCK_TCPIP_CORE(); } else { - time = SYS_ARCH_TIMEOUT; + time_needed = SYS_ARCH_TIMEOUT; } - if (time == SYS_ARCH_TIMEOUT) { + if (time_needed == SYS_ARCH_TIMEOUT) { /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message - could be fetched. We should now call the timeout handler and - deallocate the memory allocated for the timeout. */ + 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; + h = tmptimeout->h; arg = tmptimeout->arg; memp_free(MEMP_SYS_TIMEOUT, tmptimeout); if (h != NULL) { - LWIP_DEBUGF(SYS_DEBUG, ("smf calling h=%p(%p)\n", (void *)h, (void *)arg)); - h(arg); + LWIP_DEBUGF(SYS_DEBUG, ("smf calling h=%p(%p)\n", *(void**)&h, arg)); + h(arg); } /* We try again to fetch a message from the mbox. */ goto again; } else { /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout - occured. The time variable is set to the number of - milliseconds we waited for the message. */ - if (time <= timeouts->next->time) { - timeouts->next->time -= time; + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < timeouts->next->time) { + timeouts->next->time -= time_needed; } else { - timeouts->next->time = 0; + timeouts->next->time = 0; } } - } } +/** + * Wait (forever) for a semaphore to become available. + * While waiting, timeouts (for this thread) are processed. + * + * @param sem semaphore to wait for + */ void sys_sem_wait(sys_sem_t sem) { - u32_t time; + u32_t time_needed; struct sys_timeouts *timeouts; - struct sys_timeout *tmptimeout; + struct sys_timeo *tmptimeout; sys_timeout_handler h; void *arg; - /* while (sys_arch_sem_wait(sem, 1000) == 0); - return;*/ - again: timeouts = sys_arch_timeouts(); @@ -117,50 +139,60 @@ sys_sem_wait(sys_sem_t sem) sys_arch_sem_wait(sem, 0); } else { if (timeouts->next->time > 0) { - time = sys_arch_sem_wait(sem, timeouts->next->time); + time_needed = sys_arch_sem_wait(sem, timeouts->next->time); } else { - time = SYS_ARCH_TIMEOUT; + time_needed = SYS_ARCH_TIMEOUT; } - if (time == SYS_ARCH_TIMEOUT) { + if (time_needed == SYS_ARCH_TIMEOUT) { /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message - could be fetched. We should now call the timeout handler and - deallocate the memory allocated for the timeout. */ + 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); if (h != NULL) { - LWIP_DEBUGF(SYS_DEBUG, ("ssw h=%p(%p)\n", (void *)h, (void *)arg)); + LWIP_DEBUGF(SYS_DEBUG, ("ssw h=%p(%p)\n", *(void**)&h, (void *)arg)); h(arg); } - /* We try again to fetch a message from the mbox. */ goto again; } else { /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout - occured. The time variable is set to the number of - milliseconds we waited for the message. */ - if (time <= timeouts->next->time) { - timeouts->next->time -= time; + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < timeouts->next->time) { + timeouts->next->time -= time_needed; } else { - timeouts->next->time = 0; + timeouts->next->time = 0; } } - } } +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_mbox_fetch() + * - while waiting for a semaphore using sys_sem_wait() or sys_sem_wait_timeout() + * - while sleeping using the inbuilt sys_msleep() + * + * @param msecs time in milliseconds after that the timer should expire + * @param h callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ void sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg) { struct sys_timeouts *timeouts; - struct sys_timeout *timeout, *t; + struct sys_timeo *timeout, *t; timeout = memp_malloc(MEMP_SYS_TIMEOUT); if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL", timeout != NULL); return; } timeout->next = NULL; @@ -171,9 +203,12 @@ sys_timeout(u32_t msecs, sys_timeout_han timeouts = sys_arch_timeouts(); LWIP_DEBUGF(SYS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p\n", - (void *)timeout, msecs, (void *)h, (void *)arg)); + (void *)timeout, msecs, *(void**)&h, (void *)arg)); - LWIP_ASSERT("sys_timeout: timeouts != NULL", timeouts != NULL); + if (timeouts == NULL) { + LWIP_ASSERT("sys_timeout: timeouts != NULL", timeouts != NULL); + return; + } if (timeouts->next == NULL) { timeouts->next = timeout; @@ -197,89 +232,106 @@ sys_timeout(u32_t msecs, sys_timeout_han } } } - } -/* Go through timeout list (for this task only) and remove the first matching entry, - even though the timeout has not triggered yet. +/** + * Go through timeout list (for this task only) and remove the first matching + * entry, even though the timeout has not triggered yet. + * + * @note This function only works as expected if there is only one timeout + * calling 'h' in the list of timeouts. + * + * @param h callback function that would be called by the timeout + * @param arg callback argument that would be passed to h */ - void sys_untimeout(sys_timeout_handler h, void *arg) { - struct sys_timeouts *timeouts; - struct sys_timeout *prev_t, *t; + struct sys_timeouts *timeouts; + struct sys_timeo *prev_t, *t; + + timeouts = sys_arch_timeouts(); - timeouts = sys_arch_timeouts(); - - if (timeouts->next == NULL) - return; + if (timeouts == NULL) { + LWIP_ASSERT("sys_untimeout: timeouts != NULL", timeouts != NULL); + return; + } + if (timeouts->next == NULL) { + return; + } - for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next) - { - if ((t->h == h) && (t->arg == arg)) - { - /* We have a match */ - /* Unlink from previous in list */ - if (prev_t == NULL) - timeouts->next = t->next; - else - prev_t->next = t->next; - /* If not the last one, add time of this one back to next */ - if (t->next != NULL) - t->next->time += t->time; - memp_free(MEMP_SYS_TIMEOUT, t); - return; - } + for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == h) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + timeouts->next = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; } - return; + } + return; } - - - - +/** + * Timeout handler function for sys_sem_wait_timeout() + * + * @param arg struct sswt_cb* used to signal a semaphore and end waiting. + */ static void sswt_handler(void *arg) { - struct sswt_cb *sswt_cb = (struct sswt_cb *) arg; + struct sswt_cb *sswt_cb = (struct sswt_cb *) arg; - /* Timeout. Set flag to TRUE and signal semaphore */ - sswt_cb->timeflag = 1; - sys_sem_signal(*(sswt_cb->psem)); + /* Timeout. Set flag to TRUE and signal semaphore */ + sswt_cb->timeflag = 1; + sys_sem_signal(*(sswt_cb->psem)); } -/* Wait for a semaphore with timeout (specified in ms) */ -/* timeout = 0: wait forever */ -/* Returns 0 on timeout. 1 otherwise */ - +/** + * Wait for a semaphore with timeout (specified in ms) + * + * @param sem semaphore to wait + * @param timeout timeout in ms (0: wait forever) + * @return 0 on timeout, 1 otherwise + */ int sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout) { - struct sswt_cb sswt_cb; + struct sswt_cb sswt_cb; - sswt_cb.psem = &sem; - sswt_cb.timeflag = 0; + sswt_cb.psem = &sem; + sswt_cb.timeflag = 0; - /* If timeout is zero, then just wait forever */ - if (timeout > 0) - /* Create a timer and pass it the address of our flag */ - sys_timeout(timeout, sswt_handler, &sswt_cb); - sys_sem_wait(sem); - /* Was it a timeout? */ - if (sswt_cb.timeflag) - { - /* timeout */ - return 0; - } else { - /* Not a timeout. Remove timeout entry */ - sys_untimeout(sswt_handler, &sswt_cb); - return 1; - } - + /* If timeout is zero, then just wait forever */ + if (timeout > 0) { + /* Create a timer and pass it the address of our flag */ + sys_timeout(timeout, sswt_handler, &sswt_cb); + } + sys_sem_wait(sem); + /* Was it a timeout? */ + if (sswt_cb.timeflag) { + /* timeout */ + return 0; + } else { + /* Not a timeout. Remove timeout entry */ + sys_untimeout(sswt_handler, &sswt_cb); + return 1; + } } - +/** + * Sleep for some ms. Timeouts are processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ void sys_msleep(u32_t ms) { diff --git a/packages/net/lwip_tcpip/current/src/core/tcp.c b/packages/net/lwip_tcpip/current/src/core/tcp.c --- a/packages/net/lwip_tcpip/current/src/core/tcp.c +++ b/packages/net/lwip_tcpip/current/src/core/tcp.c @@ -1,6 +1,5 @@ /** * @file - * * Transmission Control Protocol for IP * * This file contains common functions for the TCP implementation, such as functinos @@ -41,24 +40,45 @@ * */ -#include +#include "lwip/opt.h" -#include "lwip/opt.h" +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + #include "lwip/def.h" #include "lwip/mem.h" #include "lwip/memp.h" - +#include "lwip/snmp.h" #include "lwip/tcp.h" -#if LWIP_TCP +#include "lwip/debug.h" +#include "lwip/stats.h" + +#include -/* Incremented every coarse grained timer shot - (typically every 500 ms, determined by TCP_COARSE_TIMEOUT). */ +const char *tcp_state_str[] = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "FIN_WAIT_1", + "FIN_WAIT_2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT" +}; + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ u32_t tcp_ticks; const u8_t tcp_backoff[13] = { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; /* The TCP PCB lists. */ +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; /** List of all TCP PCBs in LISTEN state */ union tcp_listen_pcbs_t tcp_listen_pcbs; /** List of all TCP PCBs that are in a state in which @@ -73,24 +93,6 @@ static u8_t tcp_timer; static u16_t tcp_new_port(void); /** - * Initializes the TCP layer. - */ -void -tcp_init(void) -{ - /* Clear globals. */ - tcp_listen_pcbs.listen_pcbs = NULL; - tcp_active_pcbs = NULL; - tcp_tw_pcbs = NULL; - tcp_tmp_pcb = NULL; - - /* initialize timer */ - tcp_ticks = 0; - tcp_timer = 0; - -} - -/** * Called periodically to dispatch TCP timers. * */ @@ -110,6 +112,16 @@ tcp_tmr(void) /** * Closes the connection held by the PCB. * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed */ err_t tcp_close(struct tcp_pcb *pcb) @@ -117,10 +129,10 @@ tcp_close(struct tcp_pcb *pcb) err_t err; #if TCP_DEBUG - LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in state ")); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); tcp_debug_print_state(pcb->state); - LWIP_DEBUGF(TCP_DEBUG, ("\n")); #endif /* TCP_DEBUG */ + switch (pcb->state) { case CLOSED: /* Closing a pcb in the CLOSED state might seem erroneous, @@ -131,6 +143,7 @@ tcp_close(struct tcp_pcb *pcb) * is erroneous, but this should never happen as the pcb has in those cases * been freed, and so any remaining handles are bogus. */ err = ERR_OK; + TCP_RMV(&tcp_bound_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; break; @@ -145,17 +158,26 @@ tcp_close(struct tcp_pcb *pcb) tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; + snmp_inc_tcpattemptfails(); break; case SYN_RCVD: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1; + } + break; case ESTABLISHED: err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { + snmp_inc_tcpestabresets(); pcb->state = FIN_WAIT_1; } break; case CLOSE_WAIT: err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { + snmp_inc_tcpestabresets(); pcb->state = LAST_ACK; } break; @@ -167,19 +189,28 @@ tcp_close(struct tcp_pcb *pcb) } if (pcb != NULL && err == ERR_OK) { - err = tcp_output(pcb); + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + /* @todo: When implementing SO_LINGER, this must be changed somehow: + If SOF_LINGER is set, the data should be sent when tcp_close returns. */ + tcp_output(pcb); } return err; } /** - * 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. + * Abandons a connection and optionally sends a RST to the remote + * host. Deletes the local protocol control block. This is done when + * a connection is killed because of shortage of memory. * + * @param pcb the tcp_pcb to abort + * @param reset boolean to indicate whether a reset should be sent */ void -tcp_abort(struct tcp_pcb *pcb) +tcp_abandon(struct tcp_pcb *pcb, int reset) { u32_t seqno, ackno; u16_t remote_port, local_port; @@ -221,8 +252,10 @@ tcp_abort(struct tcp_pcb *pcb) #endif /* TCP_QUEUE_OOSEQ */ memp_free(MEMP_TCP_PCB, pcb); TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); - LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abort: sending RST\n")); - tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + if (reset) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + } } } @@ -231,34 +264,63 @@ tcp_abort(struct tcp_pcb *pcb) * IP address is not given (i.e., ipaddr == NULL), the IP address of * the outgoing network interface is used instead. * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_OK if bound */ - err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) { struct tcp_pcb *cpcb; + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + if (port == 0) { port = tcp_new_port(); } /* Check if the address already is in use. */ + /* Check the listen pcbs. */ for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.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; + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; } } } + /* Check the connected pcbs. */ 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; + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + /* Check the bound, not yet connected pcbs. */ + for(cpcb = tcp_bound_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; + } + } + } + /* @todo: until SO_REUSEADDR is implemented (see task #6995 on savannah), + * we have to check the pcbs in TIME-WAIT state, also: */ + for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; } } } @@ -267,16 +329,20 @@ tcp_bind(struct tcp_pcb *pcb, struct ip_ pcb->local_ip = *ipaddr; } pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); return ERR_OK; } #if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ static err_t tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) { - (void)arg; - (void)pcb; - (void)err; + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err); return ERR_ABRT; } @@ -288,12 +354,22 @@ tcp_accept_null(void *arg, struct tcp_pc * is reallocated in order to consume less memory. Setting the * connection to LISTEN is an irreversible process. * + * @param pcb the original tcp_pcb + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory. + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen(tpcb); */ struct tcp_pcb * -tcp_listen(struct tcp_pcb *pcb) +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) { struct tcp_pcb_listen *lpcb; + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL); + /* already listening? */ if (pcb->state == LISTEN) { return pcb; @@ -310,52 +386,74 @@ tcp_listen(struct tcp_pcb *pcb) lpcb->ttl = pcb->ttl; lpcb->tos = pcb->tos; ip_addr_set(&lpcb->local_ip, &pcb->local_ip); + TCP_RMV(&tcp_bound_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); #if LWIP_CALLBACK_API lpcb->accept = tcp_accept_null; #endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + lpcb->backlog = (backlog ? backlog : 1); +#endif /* TCP_LISTEN_BACKLOG */ TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb); return (struct tcp_pcb *)lpcb; } +/** + * Update the state that tracks the available window space to advertise. + * + * Returns how much extra window would be advertised if we sent an + * update now. + */ +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) +{ + u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; + + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + /* we can advertise more window */ + pcb->rcv_ann_wnd = pcb->rcv_wnd; + return new_right_edge - pcb->rcv_ann_right_edge; + } else { + if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) { + /* Can happen due to other end sending out of advertised window, + * but within actual available (but not yet advertised) window */ + pcb->rcv_ann_wnd = 0; + } else { + /* keep the right edge of window constant */ + pcb->rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; + } + return 0; + } +} + /** * 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. * + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application */ void tcp_recved(struct tcp_pcb *pcb, u16_t len) { - if ((u32_t)pcb->rcv_wnd + len > TCP_WND) { + int wnd_inflation; + + LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n", + len <= 0xffff - pcb->rcv_wnd ); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND) pcb->rcv_wnd = TCP_WND; - } else { - pcb->rcv_wnd += len; - } - if (!(pcb->flags & TF_ACK_DELAY) && - !(pcb->flags & TF_ACK_NOW)) { - /* - * We send an ACK here (if one is not already pending, hence - * the above tests) as tcp_recved() implies that the application - * has processed some data, and so we can open the receiver's - * window to allow more to be transmitted. This could result in - * two ACKs being sent for each received packet in some limited cases - * (where the application is only receiving data, and is slow to - * process it) but it is necessary to guarantee that the sender can - * continue to transmit. - */ - tcp_ack(pcb); - } - else if (pcb->flags & TF_ACK_DELAY && pcb->rcv_wnd >= TCP_WND/2) { - /* If we can send a window update such that there is a full - * segment available in the window, do so now. This is sort of - * nagle-like in its goals, and tries to hit a compromise between - * sending acks each time the window is updated, and only sending - * window updates when a timer expires. The "threshold" used - * above (currently TCP_WND/2) can be tuned to be more or less - * aggressive */ + + wnd_inflation = tcp_update_rcv_ann_wnd(pcb); + + /* If the change in the right edge of window is significant (default + * watermark is TCP_WND/2), then send an explicit update now. + * Otherwise wait for a packet to be sent in the normal course of + * events (or more window to be available later) */ + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) tcp_ack_now(pcb); - } LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); @@ -364,6 +462,8 @@ tcp_recved(struct tcp_pcb *pcb, u16_t le /** * A nastly hack featuring 'goto' statements that allocates a * new TCP local port. + * + * @return a new (free) local TCP port number */ static u16_t tcp_new_port(void) @@ -402,15 +502,23 @@ tcp_new_port(void) * Connects to another host. The function given as the "connected" * argument will be called when the connection has been established. * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @param port the remote tcp port to connect to + * @param connected callback function to call when connected (or on error) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent */ err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port, err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) { - u32_t optdata; err_t ret; u32_t iss; + LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); if (ipaddr != NULL) { pcb->remote_ip = *ipaddr; @@ -427,23 +535,31 @@ tcp_connect(struct tcp_pcb *pcb, struct pcb->lastack = iss - 1; pcb->snd_lbb = iss - 1; pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; pcb->snd_wnd = TCP_WND; - pcb->mss = TCP_MSS; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ pcb->cwnd = 1; pcb->ssthresh = pcb->mss * 10; pcb->state = SYN_SENT; #if LWIP_CALLBACK_API pcb->connected = connected; -#endif /* LWIP_CALLBACK_API */ +#endif /* LWIP_CALLBACK_API */ + TCP_RMV(&tcp_bound_pcbs, pcb); TCP_REG(&tcp_active_pcbs, pcb); + + snmp_inc_tcpactiveopens(); - /* Build an MSS option */ - optdata = htonl(((u32_t)2 << 24) | - ((u32_t)4 << 16) | - (((u32_t)pcb->mss / 256) << 8) | - (pcb->mss & 255)); - - ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t *)&optdata, 4); + ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS +#if LWIP_TCP_TIMESTAMPS + | TF_SEG_OPTS_TS +#endif + ); if (ret == ERR_OK) { tcp_output(pcb); } @@ -454,13 +570,16 @@ tcp_connect(struct tcp_pcb *pcb, struct * 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. + * + * Automatically called from tcp_tmr(). */ void tcp_slowtmr(void) { struct tcp_pcb *pcb, *pcb2, *prev; - u32_t eff_wnd; + u16_t eff_wnd; u8_t pcb_remove; /* flag if a PCB should be removed */ + u8_t pcb_reset; /* flag if a RST should be sent when removing */ err_t err; err = ERR_OK; @@ -480,6 +599,7 @@ tcp_slowtmr(void) LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); pcb_remove = 0; + pcb_reset = 0; if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { ++pcb_remove; @@ -489,63 +609,104 @@ tcp_slowtmr(void) ++pcb_remove; LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); } else { - ++pcb->rtime; - if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { - - /* Time for a retransmission. */ - LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"U16_F" pcb->rto %"U16_F"\n", - 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]; + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); } - /* Reduce congestion window and ssthresh. */ - eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); - pcb->ssthresh = eff_wnd >> 1; - if (pcb->ssthresh < pcb->mss) { - pcb->ssthresh = pcb->mss * 2; + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) + ++pcb->rtime; + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + 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]; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_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; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); } - pcb->cwnd = pcb->mss; - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F" ssthresh %"U16_F"\n", - pcb->cwnd, pcb->ssthresh)); - - /* The following needs to be called AFTER cwnd is set to one mss - STJ */ - tcp_rexmit_rto(pcb); - } + } } /* Check if this PCB has stayed too long in FIN-WAIT-2 */ if (pcb->state == FIN_WAIT_2) { if ((u32_t)(tcp_ticks - pcb->tmr) > - TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { ++pcb_remove; LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); } } - /* Check if KEEPALIVE should be sent */ - if((pcb->so_options & SOF_KEEPALIVE) && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { - if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) { - LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n", - ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), - ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); - - tcp_abort(pcb); + /* Check if KEEPALIVE should be sent */ + if((pcb->so_options & SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { +#if LWIP_TCP_KEEPALIVE + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl)) + / TCP_SLOW_INTERVAL) +#else + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + + ++pcb_remove; + ++pcb_reset; } - else if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + pcb->keep_cnt * TCP_KEEPINTVL) / TCP_SLOW_INTERVAL) { - tcp_keepalive(pcb); - pcb->keep_cnt++; +#if LWIP_TCP_KEEPALIVE + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl) + / TCP_SLOW_INTERVAL) +#else + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT) + / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + tcp_keepalive(pcb); + pcb->keep_cnt_sent++; } - } + } /* 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 && - (u32_t)tcp_ticks - pcb->tmr >= - pcb->rto * TCP_OOSEQ_TIMEOUT) { + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { tcp_segs_free(pcb->ooseq); pcb->ooseq = NULL; LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); @@ -555,7 +716,7 @@ tcp_slowtmr(void) /* Check if this PCB has stayed too long in SYN-RCVD */ if (pcb->state == SYN_RCVD) { if ((u32_t)(tcp_ticks - pcb->tmr) > - TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { ++pcb_remove; LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); } @@ -574,7 +735,7 @@ tcp_slowtmr(void) tcp_pcb_purge(pcb); /* Remove PCB from tcp_active_pcbs list. */ if (prev != NULL) { - LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); prev->next = pcb->next; } else { /* This PCB was the first. */ @@ -583,6 +744,10 @@ tcp_slowtmr(void) } TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT); + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + } pcb2 = pcb->next; memp_free(MEMP_TCP_PCB, pcb); @@ -625,7 +790,7 @@ tcp_slowtmr(void) tcp_pcb_purge(pcb); /* Remove PCB from tcp_tw_pcbs list. */ if (prev != NULL) { - LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); prev->next = pcb->next; } else { /* This PCB was the first. */ @@ -643,15 +808,29 @@ tcp_slowtmr(void) } /** - * Is called every TCP_FAST_INTERVAL (250 ms) and sends delayed ACKs. + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). */ void tcp_fasttmr(void) { struct tcp_pcb *pcb; - /* send delayed ACKs */ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + err_t err; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); + if (err == ERR_OK) { + pcb->refused_data = NULL; + } + } + + /* send delayed ACKs */ if (pcb->flags & TF_ACK_DELAY) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); tcp_ack_now(pcb); @@ -663,6 +842,8 @@ tcp_fasttmr(void) /** * Deallocates a list of TCP segments (tcp_seg structures). * + * @param seg tcp_seg list of TCP segments to free + * @return the number of pbufs that were deallocated */ u8_t tcp_segs_free(struct tcp_seg *seg) @@ -678,8 +859,10 @@ tcp_segs_free(struct tcp_seg *seg) } /** - * Frees a TCP segment. + * Frees a TCP segment (tcp_seg structure). * + * @param seg single tcp_seg to free + * @return the number of pbufs that were deallocated */ u8_t tcp_seg_free(struct tcp_seg *seg) @@ -701,6 +884,8 @@ tcp_seg_free(struct tcp_seg *seg) /** * Sets the priority of a connection. * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority */ void tcp_setprio(struct tcp_pcb *pcb, u8_t prio) @@ -711,7 +896,10 @@ tcp_setprio(struct tcp_pcb *pcb, u8_t pr /** * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers * + * @param seg the old tcp_seg + * @return a copy of seg */ struct tcp_seg * tcp_seg_copy(struct tcp_seg *seg) @@ -722,18 +910,23 @@ tcp_seg_copy(struct tcp_seg *seg) if (cseg == NULL) { return NULL; } - memcpy((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); pbuf_ref(cseg->p); return cseg; } #endif #if LWIP_CALLBACK_API -static err_t +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { - arg = arg; + LWIP_UNUSED_ARG(arg); if (p != NULL) { + tcp_recved(pcb, p->tot_len); pbuf_free(p); } else if (err == ERR_OK) { return tcp_close(pcb); @@ -742,6 +935,11 @@ tcp_recv_null(void *arg, struct tcp_pcb } #endif /* LWIP_CALLBACK_API */ +/** + * Kills the oldest active connection that has lower priority than prio. + * + * @param prio minimum priority + */ static void tcp_kill_prio(u8_t prio) { @@ -752,8 +950,7 @@ tcp_kill_prio(u8_t prio) mprio = TCP_PRIO_MAX; - /* We kill the oldest active connection that has lower priority than - prio. */ + /* We kill the oldest active connection that has lower priority than prio. */ inactivity = 0; inactive = NULL; for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { @@ -772,7 +969,10 @@ tcp_kill_prio(u8_t prio) } } - +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ static void tcp_kill_timewait(void) { @@ -781,6 +981,7 @@ tcp_kill_timewait(void) inactivity = 0; inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { inactivity = tcp_ticks - pcb->tmr; @@ -794,8 +995,12 @@ tcp_kill_timewait(void) } } - - +/** + * Allocate a new tcp_pcb structure. + * + * @param prio priority for the new pcb + * @return a new tcp_pcb that initially is in state CLOSED + */ struct tcp_pcb * tcp_alloc(u8_t prio) { @@ -807,10 +1012,22 @@ tcp_alloc(u8_t prio) /* Try killing oldest connection in TIME-WAIT. */ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ pcb = memp_malloc(MEMP_TCP_PCB); if (pcb == NULL) { - tcp_kill_prio(prio); + /* Try killing active connections with lower priority than the new one. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio)); + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ pcb = memp_malloc(MEMP_TCP_PCB); + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed twice before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: timewait PCB was freed above */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); } } if (pcb != NULL) { @@ -819,18 +1036,20 @@ tcp_alloc(u8_t prio) pcb->snd_buf = TCP_SND_BUF; pcb->snd_queuelen = 0; pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; pcb->tos = 0; pcb->ttl = TCP_TTL; - pcb->mss = TCP_MSS; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; pcb->rto = 3000 / TCP_SLOW_INTERVAL; pcb->sa = 0; pcb->sv = 3000 / TCP_SLOW_INTERVAL; - pcb->rtime = 0; + pcb->rtime = -1; 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; @@ -842,8 +1061,14 @@ tcp_alloc(u8_t prio) #endif /* LWIP_CALLBACK_API */ /* Init KEEPALIVE timer */ - pcb->keepalive = TCP_KEEPDEFAULT; - pcb->keep_cnt = 0; + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + + pcb->keep_cnt_sent = 0; } return pcb; } @@ -851,26 +1076,28 @@ tcp_alloc(u8_t prio) /** * Creates a new TCP protocol control block but doesn't place it on * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). * * @internal: Maybe there should be a idle TCP PCB list where these - * PCBs are put on. We can then implement port reservation using - * tcp_bind(). Currently, we lack this (BSD socket type of) feature. + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED */ - struct tcp_pcb * tcp_new(void) { return tcp_alloc(TCP_PRIO_NORMAL); } -/* - * tcp_arg(): - * +/** * Used to specify the argument that should be passed callback * functions. * + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions */ - void tcp_arg(struct tcp_pcb *pcb, void *arg) { @@ -882,6 +1109,8 @@ tcp_arg(struct tcp_pcb *pcb, void *arg) * Used to specify the function that should be called when a TCP * connection receives data. * + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received */ void tcp_recv(struct tcp_pcb *pcb, @@ -894,8 +1123,9 @@ tcp_recv(struct tcp_pcb *pcb, * Used to specify the function that should be called when TCP data * has been successfully delivered to the remote host. * + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent */ - void tcp_sent(struct tcp_pcb *pcb, err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len)) @@ -907,6 +1137,9 @@ tcp_sent(struct tcp_pcb *pcb, * Used to specify the function that should be called when a fatal error * has occured on the connection. * + * @param pcb tcp_pcb to set the err callback + * @param errf callback function to call for this pcb when a fatal error + * has occured on the connection */ void tcp_err(struct tcp_pcb *pcb, @@ -919,12 +1152,15 @@ tcp_err(struct tcp_pcb *pcb, * Used for specifying the function that should be called when a * LISTENing connection has been connected to another host. * + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when 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)) { - ((struct tcp_pcb_listen *)pcb)->accept = accept; + pcb->accept = accept; } #endif /* LWIP_CALLBACK_API */ @@ -946,8 +1182,10 @@ tcp_poll(struct tcp_pcb *pcb, } /** - * Purges a TCP PCB. Removes any buffered data and frees the buffer memory. + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! */ void tcp_pcb_purge(struct tcp_pcb *pcb) @@ -957,18 +1195,48 @@ tcp_pcb_purge(struct tcp_pcb *pcb) pcb->state != LISTEN) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); - - if (pcb->unsent != NULL) { + +#if TCP_LISTEN_BACKLOG + if (pcb->state == SYN_RCVD) { + /* Need to find the corresponding listen_pcb and decrease its accepts_pending */ + struct tcp_pcb_listen *lpcb; + LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL", + tcp_listen_pcbs.listen_pcbs != NULL); + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((lpcb->local_port == pcb->local_port) && + (ip_addr_isany(&lpcb->local_ip) || + ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) { + /* port and address of the listen pcb match the timed-out pcb */ + LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending", + lpcb->accepts_pending > 0); + lpcb->accepts_pending--; + break; + } + } + } +#endif /* TCP_LISTEN_BACKLOG */ + + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); } - if (pcb->unacked != NULL) { + if (pcb->unacked != NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); } #if TCP_QUEUE_OOSEQ /* LW */ - if (pcb->ooseq != NULL) { + if (pcb->ooseq != NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); } - + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + tcp_segs_free(pcb->ooseq); pcb->ooseq = NULL; #endif /* TCP_QUEUE_OOSEQ */ @@ -981,6 +1249,8 @@ tcp_pcb_purge(struct tcp_pcb *pcb) /** * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated! */ void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) @@ -995,7 +1265,16 @@ tcp_pcb_remove(struct tcp_pcb **pcblist, pcb->flags & TF_ACK_DELAY) { pcb->flags |= TF_ACK_NOW; tcp_output(pcb); - } + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + pcb->state = CLOSED; LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); @@ -1004,6 +1283,7 @@ tcp_pcb_remove(struct tcp_pcb **pcblist, /** * Calculates a new initial sequence number for new connections. * + * @return u32_t pseudo random sequence number */ u32_t tcp_next_iss(void) @@ -1014,7 +1294,44 @@ tcp_next_iss(void) return iss; } +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calcluates the effective send mss that can be used for a specific IP address + * by using ip_route to determin the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr) +{ + u16_t mss_s; + struct netif *outif; + + outif = ip_route(addr); + if ((outif != NULL) && (outif->mtu != 0)) { + mss_s = outif->mtu - IP_HLEN - TCP_HLEN; + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * We correct for TCP options in tcp_enqueue(), and don't support + * IP options + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +const char* +tcp_debug_state_str(enum tcp_state s) +{ + return tcp_state_str[s]; +} + #if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ void tcp_debug_print(struct tcp_hdr *tcphdr) { @@ -1046,47 +1363,22 @@ tcp_debug_print(struct tcp_hdr *tcphdr) LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); } +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ void tcp_debug_print_state(enum tcp_state s) { - LWIP_DEBUGF(TCP_DEBUG, ("State: ")); - switch (s) { - case CLOSED: - LWIP_DEBUGF(TCP_DEBUG, ("CLOSED\n")); - break; - case LISTEN: - LWIP_DEBUGF(TCP_DEBUG, ("LISTEN\n")); - break; - case SYN_SENT: - LWIP_DEBUGF(TCP_DEBUG, ("SYN_SENT\n")); - break; - case SYN_RCVD: - LWIP_DEBUGF(TCP_DEBUG, ("SYN_RCVD\n")); - break; - case ESTABLISHED: - LWIP_DEBUGF(TCP_DEBUG, ("ESTABLISHED\n")); - break; - case FIN_WAIT_1: - LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_1\n")); - break; - case FIN_WAIT_2: - LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_2\n")); - break; - case CLOSE_WAIT: - LWIP_DEBUGF(TCP_DEBUG, ("CLOSE_WAIT\n")); - break; - case CLOSING: - LWIP_DEBUGF(TCP_DEBUG, ("CLOSING\n")); - break; - case LAST_ACK: - LWIP_DEBUGF(TCP_DEBUG, ("LAST_ACK\n")); - break; - case TIME_WAIT: - LWIP_DEBUGF(TCP_DEBUG, ("TIME_WAIT\n")); - break; - } + LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s])); } +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ void tcp_debug_print_flags(u8_t flags) { @@ -1114,8 +1406,12 @@ tcp_debug_print_flags(u8_t flags) if (flags & TCP_CWR) { LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); } +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ void tcp_debug_print_pcbs(void) { @@ -1143,6 +1439,9 @@ tcp_debug_print_pcbs(void) } } +/** + * Check state consistency of the tcp_pcb lists. + */ s16_t tcp_pcbs_sane(void) { @@ -1158,14 +1457,5 @@ tcp_pcbs_sane(void) return 1; } #endif /* TCP_DEBUG */ + #endif /* LWIP_TCP */ - - - - - - - - - - diff --git a/packages/net/lwip_tcpip/current/src/core/tcp_in.c b/packages/net/lwip_tcpip/current/src/core/tcp_in.c --- a/packages/net/lwip_tcpip/current/src/core/tcp_in.c +++ b/packages/net/lwip_tcpip/current/src/core/tcp_in.c @@ -1,12 +1,11 @@ /** * @file - * * Transmission Control Protocol, incoming traffic * - * The input processing functions of TCP. + * The input processing functions of the TCP layer. * - * These functions are generally called in the order (ip_input() ->) tcp_input() -> - * tcp_process() -> tcp_receive() (-> application). + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). * */ @@ -42,21 +41,22 @@ * */ -#include "lwip/def.h" #include "lwip/opt.h" +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/def.h" #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/mem.h" #include "lwip/memp.h" - #include "lwip/inet.h" -#include "lwip/tcp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" -#include "lwip/stats.h" - -#include "arch/perf.h" -#if LWIP_TCP /* These variables are global to all functions involved in the input processing of TCP segments. They are set by the tcp_input() function. */ @@ -80,15 +80,15 @@ static void tcp_parseopt(struct tcp_pcb static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); static err_t tcp_timewait_input(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()). + * + * @param p received TCP segment to process (p->payload pointing to the IP header) + * @param inp network interface on which this segment was received */ - void tcp_input(struct pbuf *p, struct netif *inp) { @@ -100,6 +100,7 @@ tcp_input(struct pbuf *p, struct netif * PERF_START; TCP_STATS_INC(tcp.recv); + snmp_inc_tcpinsegs(); iphdr = p->payload; tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); @@ -114,6 +115,7 @@ tcp_input(struct pbuf *p, struct netif * LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); TCP_STATS_INC(tcp.lenerr); TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); pbuf_free(p); return; } @@ -121,6 +123,9 @@ tcp_input(struct pbuf *p, struct netif * /* Don't even process incoming broadcasts/multicasts. */ if (ip_addr_isbroadcast(&(iphdr->dest), inp) || ip_addr_ismulticast(&(iphdr->dest))) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); pbuf_free(p); return; } @@ -138,7 +143,7 @@ tcp_input(struct pbuf *p, struct netif * #endif /* TCP_DEBUG */ TCP_STATS_INC(tcp.chkerr); TCP_STATS_INC(tcp.drop); - + snmp_inc_tcpinerrs(); pbuf_free(p); return; } @@ -147,7 +152,15 @@ tcp_input(struct pbuf *p, struct netif * /* Move the payload pointer in the pbuf so that it points to the TCP data instead of the TCP header. */ hdrlen = TCPH_HDRLEN(tcphdr); - pbuf_header(p, -(hdrlen * 4)); + if(pbuf_header(p, -(hdrlen * 4))){ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } /* Convert fields in TCP header to host byte order. */ tcphdr->src = ntohs(tcphdr->src); @@ -156,8 +169,8 @@ tcp_input(struct pbuf *p, struct netif * ackno = tcphdr->ackno = ntohl(tcphdr->ackno); tcphdr->wnd = ntohs(tcphdr->wnd); - flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS; - tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0); + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); /* Demultiplex an incoming segment. First, we check if it is destined for an active connection. */ @@ -174,13 +187,13 @@ tcp_input(struct pbuf *p, struct netif * 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). */ + lookups will be faster (we exploit locality in TCP segment + arrivals). */ LWIP_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; + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; } LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); break; @@ -191,20 +204,19 @@ tcp_input(struct pbuf *p, struct netif * if (pcb == NULL) { /* If it did not go to an active connection, we check the connections in the TIME-WAIT state. */ - for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_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)) && + 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. */ - LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); - tcp_timewait_input(pcb); - pbuf_free(p); - return; + /* 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. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb); + pbuf_free(p); + return; } } @@ -213,23 +225,23 @@ tcp_input(struct pbuf *p, struct netif * prev = NULL; for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { if ((ip_addr_isany(&(lpcb->local_ip)) || - ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && - lpcb->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) { - ((struct tcp_pcb_listen *)prev)->next = lpcb->next; - /* our successor is the remainder of the listening list */ - lpcb->next = tcp_listen_pcbs.listen_pcbs; - /* put this listening pcb at the head of the listening list */ - tcp_listen_pcbs.listen_pcbs = lpcb; - } - - LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); - tcp_listen_input(lpcb); - pbuf_free(p); - return; + ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && + lpcb->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) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb); + pbuf_free(p); + return; } prev = (struct tcp_pcb *)lpcb; } @@ -260,64 +272,89 @@ tcp_input(struct pbuf *p, struct netif * recv_data = NULL; recv_flags = 0; + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); + if (err == ERR_OK) { + pcb->refused_data = NULL; + } else { + /* drop incoming packets, because pcb is "full" */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + } tcp_input_pcb = pcb; err = tcp_process(pcb); - tcp_input_pcb = NULL; /* A return value of ERR_ABRT means that tcp_abort() was called and that the pcb has been freed. If so, we don't do anything. */ if (err != ERR_ABRT) { if (recv_flags & TF_RESET) { - /* TF_RESET means that the connection was reset by the other - end. We then call the error callback to inform the - application that the connection is dead before we - deallocate the PCB. */ - TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); - tcp_pcb_remove(&tcp_active_pcbs, pcb); - memp_free(MEMP_TCP_PCB, pcb); + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); } else if (recv_flags & TF_CLOSED) { - /* The connection has been closed and we will deallocate the - PCB. */ - tcp_pcb_remove(&tcp_active_pcbs, pcb); - memp_free(MEMP_TCP_PCB, pcb); + /* The connection has been closed and we will deallocate the + PCB. */ + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); } else { - err = ERR_OK; - /* If the application has registered a "sent" function to be - called when new send buffer space is available, we call it - now. */ - if (pcb->acked > 0) { - TCP_EVENT_SENT(pcb, pcb->acked, err); - } + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err); + } + + if (recv_data != NULL) { + if(flags & TCP_PSH) { + recv_data->flags |= PBUF_FLAG_PUSH; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); - if (recv_data != NULL) { - /* Notify application that data has been received. */ - TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); - } + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); + } + } - /* If a FIN segment was received, we call the callback - function with a NULL buffer to indicate EOF. */ - if (recv_flags & TF_GOT_FIN) { - TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); - } - /* If there were no errors, we try to send something out. */ - if (err == ERR_OK) { - tcp_output(pcb); - } + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); + } + + tcp_input_pcb = NULL; + /* Try to send something out. */ + tcp_output(pcb); +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ } } + tcp_input_pcb = NULL; - /* We deallocate the incoming pbuf. If it was buffered by the - application, the application should have called pbuf_ref() to - increase the reference counter in the pbuf. If so, the buffer - isn't actually deallocated by the call to pbuf_free(), only the - reference count is decreased. */ - if (inseg.p != NULL) pbuf_free(inseg.p); -#if TCP_INPUT_DEBUG -#if TCP_DEBUG - tcp_debug_print_state(pcb->state); -#endif /* TCP_DEBUG */ -#endif /* TCP_INPUT_DEBUG */ - + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p); + inseg.p = NULL; + } } else { /* If no matching PCB was found, send a TCP RST (reset) to the @@ -337,17 +374,23 @@ tcp_input(struct pbuf *p, struct netif * PERF_STOP("tcp_input"); } -/* tcp_listen_input(): - * +/** * Called by tcp_input() when a segment arrives for a listening - * connection. + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * @return ERR_OK if the segment was processed + * another err_t on error + * + * @note the return value is not (yet?) used in tcp_input() + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function */ - static err_t tcp_listen_input(struct tcp_pcb_listen *pcb) { struct tcp_pcb *npcb; - u32_t optdata; + err_t rc; /* In the LISTEN state, we check for incoming SYN segments, creates a new PCB, and responds with a SYN|ACK. */ @@ -360,6 +403,12 @@ tcp_listen_input(struct tcp_pcb_listen * tcphdr->dest, tcphdr->src); } else if (flags & TCP_SYN) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); + return ERR_ABRT; + } +#endif /* TCP_LISTEN_BACKLOG */ npcb = tcp_alloc(pcb->prio); /* 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 @@ -369,6 +418,9 @@ tcp_listen_input(struct tcp_pcb_listen * TCP_STATS_INC(tcp.memerr); return ERR_MEM; } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ /* Set up the new PCB. */ ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); npcb->local_port = pcb->local_port; @@ -376,6 +428,7 @@ tcp_listen_input(struct tcp_pcb_listen * npcb->remote_port = tcphdr->src; npcb->state = SYN_RCVD; npcb->rcv_nxt = seqno + 1; + npcb->rcv_ann_right_edge = npcb->rcv_nxt; npcb->snd_wnd = tcphdr->wnd; npcb->ssthresh = npcb->snd_wnd; npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ @@ -391,45 +444,83 @@ tcp_listen_input(struct tcp_pcb_listen * /* Parse any options in the SYN. */ tcp_parseopt(npcb); +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ - /* Build an MSS option. */ - optdata = htonl(((u32_t)2 << 24) | - ((u32_t)4 << 16) | - (((u32_t)npcb->mss / 256) << 8) | - (npcb->mss & 255)); + snmp_inc_tcppassiveopens(); + /* Send a SYN|ACK together with the MSS option. */ - tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4); + rc = tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, TF_SEG_OPTS_MSS +#if LWIP_TCP_TIMESTAMPS + /* and maybe include the TIMESTAMP option */ + | (npcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0) +#endif + ); + if (rc != ERR_OK) { + tcp_abandon(npcb, 0); + return rc; + } return tcp_output(npcb); } return ERR_OK; } -/* tcp_timewait_input(): - * +/** * Called by tcp_input() when a segment arrives for a connection in * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function */ - static err_t tcp_timewait_input(struct tcp_pcb *pcb) { - if (TCP_SEQ_GT(seqno + tcplen, pcb->rcv_nxt)) { - pcb->rcv_nxt = seqno + tcplen; + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return ERR_OK; } - if (tcplen > 0) { - tcp_ack_now(pcb); + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + return ERR_OK; + } + } else if (flags & TCP_FIN) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; } - return tcp_output(pcb); + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + return tcp_output(pcb); + } + return ERR_OK; } -/* 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. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function */ - static err_t tcp_process(struct tcp_pcb *pcb) { @@ -437,7 +528,6 @@ tcp_process(struct tcp_pcb *pcb) u8_t acceptable = 0; err_t err; - err = ERR_OK; /* Process incoming RST segments. */ @@ -448,10 +538,8 @@ tcp_process(struct tcp_pcb *pcb) acceptable = 1; } } else { - /*if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && - TCP_SEQ_LEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) { - */ - if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt+pcb->rcv_wnd)) { acceptable = 1; } } @@ -459,7 +547,7 @@ tcp_process(struct tcp_pcb *pcb) if (acceptable) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); - recv_flags = TF_RESET; + recv_flags |= TF_RESET; pcb->flags &= ~TF_ACK_DELAY; return ERR_RST; } else { @@ -471,9 +559,17 @@ tcp_process(struct tcp_pcb *pcb) } } + if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { + /* Cope with new connection attempt after remote end crashed */ + tcp_ack_now(pcb); + return ERR_OK; + } + /* Update the PCB (in)activity timer. */ pcb->tmr = tcp_ticks; - pcb->keep_cnt = 0; + pcb->keep_cnt_sent = 0; + + tcp_parseopt(pcb); /* Do different things depending on the TCP state. */ switch (pcb->state) { @@ -485,24 +581,42 @@ tcp_process(struct tcp_pcb *pcb) && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { pcb->snd_buf++; pcb->rcv_nxt = seqno + 1; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; pcb->lastack = ackno; pcb->snd_wnd = tcphdr->wnd; pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ pcb->state = ESTABLISHED; - pcb->cwnd = pcb->mss; + +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + /* Set ssthresh again after changing pcb->mss (already set in tcp_connect + * but for the default value of pcb->mss) */ + pcb->ssthresh = pcb->mss * 10; + + pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); --pcb->snd_queuelen; LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); rseg = pcb->unacked; pcb->unacked = rseg->next; - tcp_seg_free(rseg); - /* Parse any options in the SYNACK. */ - tcp_parseopt(pcb); + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else { + pcb->rtime = 0; + pcb->nrtx = 0; + } + + tcp_seg_free(rseg); /* Call the user specified function to call when sucessfully * connected. */ TCP_EVENT_CONNECTED(pcb, ERR_OK, err); - tcp_ack(pcb); + tcp_ack_now(pcb); } /* received ACK? possibly a half-open connection */ else if (flags & TCP_ACK) { @@ -512,10 +626,10 @@ tcp_process(struct tcp_pcb *pcb) } break; case SYN_RCVD: - if (flags & TCP_ACK && - !(flags & TCP_RST)) { + if (flags & TCP_ACK) { /* expected ACK number? */ if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + u16_t old_cwnd; pcb->state = ESTABLISHED; LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); #if LWIP_CALLBACK_API @@ -529,34 +643,49 @@ tcp_process(struct tcp_pcb *pcb) tcp_abort(pcb); return ERR_ABRT; } + old_cwnd = pcb->cwnd; /* 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; + + /* Prevent ACK for SYN to generate a sent event */ + if (pcb->acked != 0) { + pcb->acked--; + } + + pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + + if (recv_flags & TF_GOT_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } } /* incorrect ACK number */ else { /* send RST */ tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), - tcphdr->dest, tcphdr->src); + tcphdr->dest, tcphdr->src); } + } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { + /* Looks like another copy of the SYN - retransmit our SYN-ACK */ + tcp_rexmit(pcb); } break; case CLOSE_WAIT: /* FALLTHROUGH */ case ESTABLISHED: tcp_receive(pcb); - if (flags & TCP_FIN) { + if (recv_flags & TF_GOT_FIN) { /* passive close */ 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) { + if (recv_flags & TF_GOT_FIN) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { LWIP_DEBUGF(TCP_DEBUG, - ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); tcp_pcb_purge(pcb); TCP_RMV(&tcp_active_pcbs, pcb); @@ -566,14 +695,14 @@ tcp_process(struct tcp_pcb *pcb) tcp_ack_now(pcb); pcb->state = CLOSING; } - } else if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + } 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) { - LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + if (recv_flags & TF_GOT_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); tcp_pcb_purge(pcb); TCP_RMV(&tcp_active_pcbs, pcb); @@ -584,8 +713,7 @@ tcp_process(struct tcp_pcb *pcb) case CLOSING: tcp_receive(pcb); if (flags & TCP_ACK && ackno == pcb->snd_nxt) { - LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); - tcp_ack_now(pcb); + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_pcb_purge(pcb); TCP_RMV(&tcp_active_pcbs, pcb); pcb->state = TIME_WAIT; @@ -595,9 +723,9 @@ tcp_process(struct tcp_pcb *pcb) case LAST_ACK: tcp_receive(pcb); if (flags & TCP_ACK && ackno == pcb->snd_nxt) { - LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); - pcb->state = CLOSED; - recv_flags = TF_CLOSED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags |= TF_CLOSED; } break; default: @@ -606,8 +734,48 @@ tcp_process(struct tcp_pcb *pcb) return ERR_OK; } -/* tcp_receive: +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) * + * Called from tcp_receive() + */ +static void +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + struct tcp_seg *old_seg; + + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } + else { + /* delete some following segments + oos queue may have segments with FIN flag */ + while (next && + TCP_SEQ_GEQ((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + /* cseg with FIN already processed */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + TCPH_FLAGS_SET(cseg->tcphdr, TCPH_FLAGS(cseg->tcphdr) | TCP_FIN); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif + +/** * 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 @@ -616,8 +784,9 @@ tcp_process(struct tcp_pcb *pcb) * * If the incoming segment constitutes an ACK for a segment that was used for RTT * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). */ - static void tcp_receive(struct tcp_pcb *pcb) { @@ -630,10 +799,10 @@ tcp_receive(struct tcp_pcb *pcb) s16_t m; u32_t right_wnd_edge; u16_t new_tot_len; - + int found_dupack = 0; if (flags & TCP_ACK) { - right_wnd_edge = pcb->snd_wnd + pcb->snd_wl1; + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; /* Update window. */ if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || @@ -642,59 +811,77 @@ tcp_receive(struct tcp_pcb *pcb) pcb->snd_wnd = tcphdr->wnd; pcb->snd_wl1 = seqno; pcb->snd_wl2 = ackno; - LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U32_F"\n", pcb->snd_wnd)); + if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) { + pcb->persist_backoff = 0; + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); #if TCP_WND_DEBUG } else { if (pcb->snd_wnd != tcphdr->wnd) { - LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: no window update lastack %"U32_F" snd_max %"U32_F" ackno %"U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", - pcb->lastack, pcb->snd_max, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + LWIP_DEBUGF(TCP_WND_DEBUG, + ("tcp_receive: no window update lastack %"U32_F" ackno %" + U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); } #endif /* TCP_WND_DEBUG */ } - - if (pcb->lastack == ackno) { - pcb->acked = 0; + /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a + * duplicate ack if: + * 1) It doesn't ACK new data + * 2) length of received packet is zero (i.e. no payload) + * 3) the advertised window hasn't changed + * 4) There is outstanding unacknowledged data (retransmission timer running) + * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) + * + * If it passes all five, should process as a dupack: + * a) dupacks < 3: do nothing + * b) dupacks == 3: fast retransmit + * c) dupacks > 3: increase cwnd + * + * If it only passes 1-3, should reset dupack counter (and add to + * stats, which we don't do in lwIP) + * + * If it only passes 1, should reset dupack counter + * + */ - if (pcb->snd_wl1 + pcb->snd_wnd == right_wnd_edge){ - ++pcb->dupacks; - if (pcb->dupacks >= 3 && pcb->unacked != NULL) { - if (!(pcb->flags & TF_INFR)) { - /* This is fast retransmit. Retransmit the first unacked segment. */ - LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %"U16_F" (%"U32_F"), fast retransmit %"U32_F"\n", - (u16_t)pcb->dupacks, pcb->lastack, - ntohl(pcb->unacked->tcphdr->seqno))); - tcp_rexmit(pcb); - /* Set ssthresh to max (FlightSize / 2, 2*SMSS) */ - /*pcb->ssthresh = LWIP_MAX((pcb->snd_max - - pcb->lastack) / 2, - 2 * pcb->mss);*/ - /* Set ssthresh to half of the minimum of the currenct cwnd and the advertised window */ - if(pcb->cwnd > pcb->snd_wnd) - pcb->ssthresh = pcb->snd_wnd / 2; - else - pcb->ssthresh = pcb->cwnd / 2; - - 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 ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { - pcb->cwnd += pcb->mss; + /* Clause 1 */ + if (TCP_SEQ_LEQ(ackno, pcb->lastack)) { + pcb->acked = 0; + /* Clause 2 */ + if (tcplen == 0) { + /* Clause 3 */ + if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ + /* Clause 4 */ + if (pcb->rtime >= 0) { + /* Clause 5 */ + if (pcb->lastack == ackno) { + found_dupack = 1; + if (pcb->dupacks + 1 > pcb->dupacks) + ++pcb->dupacks; + if (pcb->dupacks > 3) { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) { + /* Do fast retransmit */ + tcp_rexmit_fast(pcb); + } } } } - } else { - LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupack averted %"U32_F" %"U32_F"\n", - pcb->snd_wl1 + pcb->snd_wnd, right_wnd_edge)); } - } else - /*if (TCP_SEQ_LT(pcb->lastack, ackno) && - TCP_SEQ_LEQ(ackno, pcb->snd_max)) { */ - if(TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_max)){ + /* If Clause (1) or more is true, but not a duplicate ack, reset + * count of consecutive duplicate acks */ + if (!found_dupack) { + pcb->dupacks = 0; + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){ /* 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. */ @@ -709,8 +896,8 @@ tcp_receive(struct tcp_pcb *pcb) /* Reset the retransmission time-out. */ pcb->rto = (pcb->sa >> 3) + pcb->sv; - /* Update the send buffer space. */ - pcb->acked = ackno - pcb->lastack; + /* Update the send buffer space. Diff between the two can never exceed 64K? */ + pcb->acked = (u16_t)(ackno - pcb->lastack); pcb->snd_buf += pcb->acked; @@ -755,6 +942,12 @@ tcp_receive(struct tcp_pcb *pcb) pcb->unacked = pcb->unacked->next; LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + pcb->snd_queuelen -= pbuf_clen(next->p); tcp_seg_free(next); @@ -764,7 +957,18 @@ tcp_receive(struct tcp_pcb *pcb) pcb->unsent != NULL); } } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else + pcb->rtime = 0; + pcb->polltmr = 0; + } else { + /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; } /* We go through the ->unsent list to see if any of the segments @@ -774,10 +978,8 @@ tcp_receive(struct tcp_pcb *pcb) ->unsent list after a retransmission, so these segments may in fact have been sent once. */ while (pcb->unsent != NULL && - /*TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), ackno) && - TCP_SEQ_LEQ(ackno, pcb->snd_max)*/ - TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), pcb->snd_max) - ) { + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent))); @@ -785,6 +987,11 @@ tcp_receive(struct tcp_pcb *pcb) next = pcb->unsent; pcb->unsent = pcb->unsent->next; LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } pcb->snd_queuelen -= pbuf_clen(next->p); tcp_seg_free(next); LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); @@ -792,10 +999,6 @@ tcp_receive(struct tcp_pcb *pcb) LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } - - if (pcb->unsent != NULL) { - pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno); - } } /* End of ACK for new data processing. */ @@ -806,7 +1009,9 @@ tcp_receive(struct tcp_pcb *pcb) 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; + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest); LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", m, m * TCP_SLOW_INTERVAL)); @@ -821,7 +1026,7 @@ tcp_receive(struct tcp_pcb *pcb) pcb->sv += m; pcb->rto = (pcb->sa >> 3) + pcb->sv; - LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" miliseconds)\n", + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); pcb->rttest = 0; @@ -861,13 +1066,13 @@ tcp_receive(struct tcp_pcb *pcb) segment is larger than rcv_nxt. */ /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ - if(TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno+1, seqno+tcplen-1)){ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){ /* 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 @@ -877,15 +1082,18 @@ tcp_receive(struct tcp_pcb *pcb) 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; p = inseg.p; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); if (inseg.p->len < off) { - new_tot_len = inseg.p->tot_len - off; + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); while (p->len < off) { off -= p->len; /* KJM following line changed (with addition of new_tot_len var) @@ -895,21 +1103,27 @@ tcp_receive(struct tcp_pcb *pcb) p->len = 0; p = p->next; } - pbuf_header(p, -off); + if(pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } } else { - pbuf_header(inseg.p, -off); + if(pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } } /* KJM following line changed to use p->payload rather than inseg->p->payload to fix bug #9076 */ inseg.dataptr = p->payload; - inseg.len -= pcb->rcv_nxt - seqno; + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; } - else{ - if(TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ /* the whole segment is < rcv_nxt */ /* must be a duplicate of a packet that has already been correctly handled */ - + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); tcp_ack_now(pcb); } @@ -918,38 +1132,102 @@ tcp_receive(struct tcp_pcb *pcb) /* 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(TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)){ 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. */ + tcplen = TCP_TCPLEN(&inseg); + + if (tcplen > pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U32_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + inseg.len = pcb->rcv_wnd; + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } #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); + if (pcb->ooseq != NULL) { + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: received in-order FIN, binning ooseq queue\n")); + /* Received in-order FIN means anything that was received + * out of order must now have been received in-order, so + * bin the ooseq queue + * rcv_nxt + * . |--ooseq--| + * .==seg============|FIN + */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } + else { + struct tcp_seg* next = pcb->ooseq; + struct tcp_seg *old_seg; + /* rcv_nxt + * . |--ooseq--| + * .==seg============| + */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg doesn't have FIN (already processed) */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_FLAGS_SET(inseg.tcphdr, + TCPH_FLAGS(inseg.tcphdr) | TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + /* rcv_nxt + * . |--ooseq--| + * .==seg============| + */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* FIN in inseg already handled by dropping whole ooseq queue */ + inseg.len = (u16_t)(pcb->ooseq->tcphdr->seqno - seqno); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n", + (seqno + tcplen) == pcb->ooseq->tcphdr->seqno); + } + pcb->ooseq = next; + } } #endif /* TCP_QUEUE_OOSEQ */ - tcplen = TCP_TCPLEN(&inseg); - - /* First received FIN will be ACKed +1, on any successive (duplicate) - * FINs we are already in CLOSE_WAIT and have already done +1. - */ - if (pcb->state != CLOSE_WAIT) { - pcb->rcv_nxt += tcplen; - } + pcb->rcv_nxt = seqno + tcplen; /* Update the receiver's (our) window. */ - if (pcb->rcv_wnd < tcplen) { - pcb->rcv_wnd = 0; - } else { - pcb->rcv_wnd -= tcplen; - } + LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen); + pcb->rcv_wnd -= tcplen; + + tcp_update_rcv_ann_wnd(pcb); /* If there is data in the segment, we make preparations to pass this up to the application. The ->recv_data variable @@ -969,7 +1247,7 @@ tcp_receive(struct tcp_pcb *pcb) } if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); - recv_flags = TF_GOT_FIN; + recv_flags |= TF_GOT_FIN; } #if TCP_QUEUE_OOSEQ @@ -982,11 +1260,12 @@ tcp_receive(struct tcp_pcb *pcb) 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); - } + LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n", + pcb->rcv_wnd >= TCP_TCPLEN(cseg)); + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + + tcp_update_rcv_ann_wnd(pcb); + if (cseg->p->tot_len > 0) { /* Chain this pbuf onto the pbuf that we will pass to the application. */ @@ -999,10 +1278,12 @@ tcp_receive(struct tcp_pcb *pcb) } if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); - recv_flags = TF_GOT_FIN; + recv_flags |= TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } } - pcb->ooseq = cseg->next; tcp_seg_free(cseg); } @@ -1014,7 +1295,7 @@ tcp_receive(struct tcp_pcb *pcb) } else { /* We get here if the incoming segment is out-of-sequence. */ - tcp_ack_now(pcb); + tcp_send_empty_ack(pcb); #if TCP_QUEUE_OOSEQ /* We queue the segment on the ->ooseq queue. */ if (pcb->ooseq == NULL) { @@ -1041,16 +1322,16 @@ tcp_receive(struct tcp_pcb *pcb) discard. */ if (inseg.len > next->len) { /* The incoming segment is larger than the old - segment. We replace the old segment with the new + segment. We replace some segments 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; } + tcp_oos_insert_segment(cseg, next); } break; } else { @@ -1066,56 +1347,49 @@ tcp_receive(struct tcp_pcb *pcb) 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; + tcp_oos_insert_segment(cseg, next); } break; } - } else + } else { /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ - if(TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)){ - /* 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); + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; } - - 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)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } 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; + next->len = (u16_t)(seqno - next->tcphdr->seqno); pbuf_realloc(next->p, next->len); } } @@ -1129,11 +1403,8 @@ tcp_receive(struct tcp_pcb *pcb) } } else { - /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || - TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ - if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ - tcp_ack_now(pcb); - } + /* The incoming segment is not withing the window. */ + tcp_send_empty_ack(pcb); } } else { /* Segments with length 0 is taken care of here. Segments that @@ -1146,46 +1417,83 @@ tcp_receive(struct tcp_pcb *pcb) } } -/* - * tcp_parseopt: +/** + * Parses the options contained in the incoming segment. * - * Parses the options contained in the incoming segment. (Code taken - * from uIP with only small changes.) + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! * + * @param pcb the tcp_pcb for which a segment arrived */ - static void tcp_parseopt(struct tcp_pcb *pcb) { - u8_t c; + u16_t c, max_c; + u16_t mss; u8_t *opts, opt; - u16_t mss; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif opts = (u8_t *)tcphdr + TCP_HLEN; /* Parse the TCP MSS option, if present. */ if(TCPH_HDRLEN(tcphdr) > 0x5) { - for(c = 0; c < (TCPH_HDRLEN(tcphdr) - 5) << 2 ;) { + max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2; + for (c = 0; c < max_c; ) { opt = opts[c]; - if (opt == 0x00) { + switch (opt) { + case 0x00: /* End of options. */ - break; - } else if (opt == 0x01) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case 0x01: + /* NOP option. */ ++c; - /* NOP option. */ - } else if (opt == 0x02 && - opts[c + 1] == 0x04) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case 0x02: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (opts[c + 1] != 0x04 || c + 0x04 > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } /* 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. */ + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + /* Advance to next option */ + c += 0x04; break; - } else { - if (opts[c + 1] == 0) { +#if LWIP_TCP_TIMESTAMPS + case 0x08: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (opts[c + 1] != 0x0A || c + 0x0A > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = (opts[c+2]) | (opts[c+3] << 8) | + (opts[c+4] << 16) | (opts[c+5] << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = ntohl(tsval); + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = ntohl(tsval); + } + /* Advance to next option */ + c += 0x0A; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + if (opts[c + 1] == 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); /* If the length field is zero, the options are malformed and we don't process them further. */ - break; + return; } /* All other options have a length field, so that we easily can skip past them. */ @@ -1194,6 +1502,5 @@ tcp_parseopt(struct tcp_pcb *pcb) } } } + #endif /* LWIP_TCP */ - - diff --git a/packages/net/lwip_tcpip/current/src/core/tcp_out.c b/packages/net/lwip_tcpip/current/src/core/tcp_out.c --- a/packages/net/lwip_tcpip/current/src/core/tcp_out.c +++ b/packages/net/lwip_tcpip/current/src/core/tcp_out.c @@ -1,6 +1,5 @@ /** * @file - * * Transmission Control Protocol, outgoing traffic * * The output functions of TCP. @@ -39,29 +38,60 @@ * */ -#include +#include "lwip/opt.h" +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" #include "lwip/def.h" -#include "lwip/opt.h" #include "lwip/mem.h" #include "lwip/memp.h" #include "lwip/sys.h" #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/inet.h" -#include "lwip/tcp.h" +#include "lwip/inet_chksum.h" #include "lwip/stats.h" +#include "lwip/snmp.h" -#if LWIP_TCP +#include /* Forward declarations.*/ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); +static struct tcp_hdr * +tcp_output_set_header(struct tcp_pcb *pcb, struct pbuf *p, int optlen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seqno_be; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_FLAGS_SET(tcphdr, TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, (5 + optlen / 4)); + tcphdr->chksum = 0; + + /* If we're sending a packet, update the announced right window edge */ + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + return tcphdr; +} + +/** + * Called by tcp_close() to send a segment including flags but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @param flags the flags to set in the segment header + * @return ERR_OK if sent, another err_t otherwise + */ err_t tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags) { - /* no data, no length, flags, copy=1, no optdata, no optdatalen */ - return tcp_enqueue(pcb, NULL, 0, flags, 1, NULL, 0); + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, 0); } /** @@ -72,71 +102,88 @@ tcp_send_ctrl(struct tcp_pcb *pcb, u8_t * To prompt the system to send data now, call tcp_output() after * calling tcp_write(). * - * @arg pcb Protocol control block of the TCP connection to enqueue data for. + * @param pcb Protocol control block of the TCP connection to enqueue data for. + * @param data pointer to the data to send + * @param len length (in bytes) of the data to send + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error * * @see tcp_write() */ - err_t -tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t copy) +tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%"U16_F", copy=%"U16_F")\n", (void *)pcb, - arg, len, (u16_t)copy)); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", (void *)pcb, + data, len, (u16_t)apiflags)); /* connection is in valid state for data transmission? */ if (pcb->state == ESTABLISHED || pcb->state == CLOSE_WAIT || pcb->state == SYN_SENT || pcb->state == SYN_RCVD) { if (len > 0) { - return tcp_enqueue(pcb, (void *)arg, len, 0, copy, NULL, 0); +#if LWIP_TCP_TIMESTAMPS + return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, + pcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0); +#else + return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, 0); +#endif } return ERR_OK; } else { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_STATE | 3, ("tcp_write() called in invalid state\n")); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); return ERR_CONN; } } /** - * Enqueue either data or TCP options (but not both) for tranmission - * - * - * - * @arg pcb Protocol control block for the TCP connection to enqueue data for. - * @arg arg Pointer to the data to be enqueued for sending. - * @arg len Data length in bytes - * @arg flags - * @arg copy 1 if data must be copied, 0 if data is non-volatile and can be - * referenced. - * @arg optdata - * @arg optlen + * Enqueue data and/or TCP options for transmission + * + * Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for. + * @param arg Pointer to the data to be enqueued for sending. + * @param len Data length in bytes + * @param flags tcp header flags to set in the outgoing segment + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @param optflags options to include in segment later on (see definition of struct tcp_seg) */ err_t tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, - u8_t flags, u8_t copy, - u8_t *optdata, u8_t optlen) + u8_t flags, u8_t apiflags, u8_t optflags) { struct pbuf *p; struct tcp_seg *seg, *useg, *queue; - u32_t left, seqno; - u16_t seglen; + u32_t seqno; + u16_t left, seglen; void *ptr; - u8_t queuelen; + u16_t queuelen; + u8_t optlen; - LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", copy=%"U16_F")\n", - (void *)pcb, arg, len, (u16_t)flags, (u16_t)copy)); - LWIP_ASSERT("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)", - len == 0 || optlen == 0); - LWIP_ASSERT("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)", - arg == NULL || optdata == NULL); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags)); + LWIP_ERROR("tcp_enqueue: packet needs payload, options, or SYN/FIN (programmer violates API)", + ((len != 0) || (optflags != 0) || ((flags & (TCP_SYN | TCP_FIN)) != 0)), + return ERR_ARG;); + LWIP_ERROR("tcp_enqueue: len != 0 || arg == NULL (programmer violates API)", + ((len != 0) || (arg == NULL)), return ERR_ARG;); + /* fail on too much data */ if (len > pcb->snd_buf) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } left = len; ptr = arg; + optlen = LWIP_TCP_OPT_LENGTH(optflags); + /* seqno will be the sequence number of the first segment enqueued * by the call to this function. */ seqno = pcb->snd_lbb; @@ -146,9 +193,12 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a /* If total number of pbufs on the unsent/unacked queues exceeds the * configured maximum, return an error */ queuelen = pcb->snd_queuelen; - if (queuelen >= TCP_SND_QUEUELEN) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + /* check for configured max queuelen and possible overflow */ + if ((queuelen >= TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } if (queuelen != 0) { @@ -164,15 +214,14 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a useg = queue = seg = NULL; seglen = 0; while (queue == NULL || left > 0) { - - /* The segment length should be the MSS if the data to be enqueued - * is larger than the MSS. */ - seglen = left > pcb->mss? pcb->mss: left; + /* The segment length (including options) should be at most the MSS */ + seglen = left > (pcb->mss - optlen) ? (pcb->mss - optlen) : left; /* Allocate memory for tcp_seg, and fill in fields. */ seg = memp_malloc(MEMP_TCP_SEG); if (seg == NULL) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for tcp_seg\n")); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: could not allocate memory for tcp_seg\n")); goto memerr; } seg->next = NULL; @@ -193,64 +242,61 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a /* 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. */ - - /* options? */ - if (optdata != NULL) { - if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + * ROM or other static memory, and need not be copied. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen + optlen, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); goto memerr; } - ++queuelen; - seg->dataptr = seg->p->payload; - } - /* copy from volatile memory? */ - else if (copy) { - if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); - goto memerr; - } - ++queuelen; + LWIP_ASSERT("check that first pbuf can hold the complete seglen", + (seg->p->len >= seglen + optlen)); + queuelen += pbuf_clen(seg->p); if (arg != NULL) { - memcpy(seg->p->payload, ptr, seglen); + MEMCPY((char *)seg->p->payload + optlen, ptr, seglen); } seg->dataptr = seg->p->payload; } /* do not copy data */ else { - /* First, allocate a pbuf for holding the data. + /* First, allocate a pbuf for the headers. */ + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: could not allocate memory for header pbuf\n")); + goto memerr; + } + queuelen += pbuf_clen(seg->p); + + /* Second, allocate a pbuf for holding the data. * since the referenced data is available at least until it is sent out on the * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM * instead of PBUF_REF here. */ - if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); - goto memerr; - } - ++queuelen; - /* reference the non-volatile payload data */ - p->payload = ptr; - seg->dataptr = ptr; + if (left > 0) { + if ((p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + /* If allocation fails, we have to deallocate the header pbuf as well. */ + pbuf_free(seg->p); + seg->p = NULL; + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } + ++queuelen; + /* reference the non-volatile payload data */ + p->payload = ptr; + seg->dataptr = ptr; - /* Second, allocate a pbuf for the headers. */ - if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { - /* If allocation fails, we have to deallocate the data pbuf as - * well. */ - pbuf_free(p); - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n")); - goto memerr; + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(seg->p/*header*/, p/*data*/); + p = NULL; } - ++queuelen; - - /* Concatenate the headers and data pbufs together. */ - pbuf_cat(seg->p/*header*/, p/*data*/); - p = NULL; } /* Now that there are more segments queued, we check again if the - length of the queue exceeds the configured maximum. */ - if (queuelen > TCP_SND_QUEUELEN) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + length of the queue exceeds the configured maximum or overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); goto memerr; } @@ -258,7 +304,7 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a /* build TCP header */ if (pbuf_header(seg->p, TCP_HLEN)) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n")); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_enqueue: no room for TCP header in pbuf.\n")); TCP_STATS_INC(tcp.err); goto memerr; } @@ -270,18 +316,11 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a TCPH_FLAGS_SET(seg->tcphdr, flags); /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ - /* Copy the options into the header, if they are present. */ - if (optdata == NULL) { - TCPH_HDRLEN_SET(seg->tcphdr, 5); - } - else { - TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); - /* Copy options into data portion of segment. - Options can thus only be sent in non data carrying - segments such as SYN|ACK. */ - memcpy(seg->dataptr, optdata, optlen); - } - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + seg->flags = optflags; + + /* Set the length of the header */ + TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", ntohl(seg->tcphdr->seqno), ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), (u16_t)flags)); @@ -307,18 +346,43 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a if (useg != NULL && TCP_TCPLEN(useg) != 0 && !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && - !(flags & (TCP_SYN | TCP_FIN)) && + (!(flags & (TCP_SYN | TCP_FIN)) || (flags == TCP_FIN)) && /* fit within max seg size */ - useg->len + queue->len <= pcb->mss) { + (useg->len + queue->len <= pcb->mss) && + /* only concatenate segments with the same options */ + (useg->flags == queue->flags) && + /* segments are consecutive */ + (ntohl(useg->tcphdr->seqno) + useg->len == ntohl(queue->tcphdr->seqno)) ) { /* Remove TCP header from first segment of our to-be-queued list */ - pbuf_header(queue->p, -TCP_HLEN); - pbuf_cat(useg->p, queue->p); - useg->len += queue->len; - useg->next = queue->next; + if(pbuf_header(queue->p, -(TCP_HLEN + optlen))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + TCP_STATS_INC(tcp.err); + goto memerr; + } + if (queue->p->len == 0) { + /* free the first (header-only) pbuf if it is now empty (contained only headers) */ + struct pbuf *old_q = queue->p; + queue->p = queue->p->next; + old_q->next = NULL; + queuelen--; + pbuf_free(old_q); + } + if (flags & TCP_FIN) { + /* the new segment contains only FIN, no data -> put the FIN into the last segment */ + LWIP_ASSERT("FIN enqueued together with data", queue->p == NULL && queue->len == 0); + TCPH_SET_FLAG(useg->tcphdr, TCP_FIN); + } else { + LWIP_ASSERT("zero-length pbuf", (queue->p != NULL) && (queue->p->len > 0)); + pbuf_cat(useg->p, queue->p); + useg->len += queue->len; + useg->next = queue->next; + } - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE | DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len)); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len)); if (seg == queue) { - seg = NULL; + seg = useg; + seglen = useg->len; } memp_free(MEMP_TCP_SEG, queue); } @@ -336,6 +400,9 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a if ((flags & TCP_SYN) || (flags & TCP_FIN)) { ++len; } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } pcb->snd_lbb += len; pcb->snd_buf -= len; @@ -350,12 +417,13 @@ tcp_enqueue(struct tcp_pcb *pcb, void *a /* 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) { + if (seg != NULL && seglen > 0 && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); } return ERR_OK; memerr: + pcb->flags |= TF_NAGLEMEMERR; TCP_STATS_INC(tcp.memerr); if (queue != NULL) { @@ -365,18 +433,92 @@ memerr: LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } - LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); return ERR_MEM; } -/* find out what we can send and send it */ + +#if LWIP_TCP_TIMESTAMPS +/* Build a timestamp option (12 bytes long) at the specified options pointer) + * + * @param pcb tcp_pcb + * @param opts option pointer where to store the timestamp option + */ +static void +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = htonl(0x0101080A); + opts[1] = htonl(sys_now()); + opts[2] = htonl(pcb->ts_recent); +} +#endif + +/** Send an ACK without data. + * + * @param pcb Protocol control block for the TCP connection to send the ACK + */ +err_t +tcp_send_empty_ack(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + u8_t optlen = 0; + +#if LWIP_TCP_TIMESTAMPS + if (pcb->flags & TF_TIMESTAMP) { + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif + p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + tcphdr = tcp_output_set_header(pcb, p, optlen, htonl(pcb->snd_nxt)); + + /* NB. MSS option is only sent on SYNs, so ignore it here */ +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (pcb->flags & TF_TIMESTAMP) { + tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); + } +#endif + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), + IP_PROTO_TCP, p->tot_len); +#endif +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + pbuf_free(p); + + return ERR_OK; +} + +/** + * Find out what we can send and send it + * + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ err_t tcp_output(struct tcp_pcb *pcb) { - struct pbuf *p; - struct tcp_hdr *tcphdr; struct tcp_seg *seg, *useg; - u32_t wnd; + u32_t wnd, snd_nxt; #if TCP_CWND_DEBUG s16_t i = 0; #endif /* TCP_CWND_DEBUG */ @@ -393,12 +535,6 @@ tcp_output(struct tcp_pcb *pcb) seg = pcb->unsent; - /* useg should point to last segment on unacked queue */ - useg = pcb->unacked; - if (useg != NULL) { - for (; useg->next != NULL; useg = useg->next); - } - /* If the TF_ACK_NOW flag is set and no data will be sent (either * because the ->unsent queue is empty or because the window does * not allow it), construct an empty ACK segment and send it. @@ -408,59 +544,54 @@ tcp_output(struct tcp_pcb *pcb) if (pcb->flags & TF_ACK_NOW && (seg == NULL || ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { - p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); - if (p == NULL) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); - return ERR_BUF; - } - LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); - /* remove ACK flags from the PCB, as we send an empty ACK now */ - pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + return tcp_send_empty_ack(pcb); + } - 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_HDRLEN_SET(tcphdr, 5); - - tcphdr->chksum = 0; -#if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), - IP_PROTO_TCP, p->tot_len); -#endif - ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, - IP_PROTO_TCP); - pbuf_free(p); - - return ERR_OK; + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next); } #if TCP_OUTPUT_DEBUG if (seg == NULL) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", (void*)pcb->unsent)); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); } #endif /* TCP_OUTPUT_DEBUG */ #if TCP_CWND_DEBUG if (seg == NULL) { - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", seg == NULL, ack %"U32_F"\n", - pcb->snd_wnd, pcb->cwnd, wnd, - pcb->lastack)); + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); } else { - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", - pcb->snd_wnd, pcb->cwnd, wnd, - ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, - ntohl(seg->tcphdr->seqno), pcb->lastack)); + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\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 */ /* data available and window allows it to be sent? */ while (seg != NULL && - ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_enqueue had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_enqueue/tcp_output. + */ + if((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ + break; + } #if TCP_CWND_DEBUG - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", pcb->snd_wnd, pcb->cwnd, wnd, ntohl(seg->tcphdr->seqno) + seg->len - pcb->lastack, @@ -476,9 +607,9 @@ tcp_output(struct tcp_pcb *pcb) } 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; + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; } /* put segment on unacknowledged list if length > 0 */ if (TCP_TCPLEN(seg) > 0) { @@ -490,12 +621,17 @@ tcp_output(struct tcp_pcb *pcb) /* unacked list is not empty? */ } else { /* In the case of fast retransmit, the packet should not go to the tail - * of the unacked queue, but rather at the head. We need to check for + * of the unacked queue, but rather somewhere before it. We need to check for * this case. -STJ Jul 27, 2004 */ if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){ - /* add segment to head of unacked list */ - seg->next = pcb->unacked; - pcb->unacked = seg; + /* add segment to before tail of unacked list, keeping the list sorted */ + struct tcp_seg **cur_seg = &(pcb->unacked); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = (*cur_seg); + (*cur_seg) = seg; } else { /* add segment to tail of unacked list */ useg->next = seg; @@ -508,29 +644,58 @@ tcp_output(struct tcp_pcb *pcb) } seg = pcb->unsent; } + + if (seg != NULL && pcb->persist_backoff == 0 && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) { + /* prepare for persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + + pcb->flags &= ~TF_NAGLEMEMERR; return ERR_OK; } /** - * Actually send a TCP segment over IP + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment */ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) { u16_t len; struct netif *netif; + u32_t *opts; + + /** @bug Exclude retransmitted segments from this count. */ + snmp_inc_tcpoutsegs(); /* 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 { - /* advertise our receive window size in this TCP segment */ - seg->tcphdr->wnd = htons(pcb->rcv_wnd); + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + /* Add any requested options. NB MSS option is only set on SYN + packets, so ignore it here */ + opts = (u32_t *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + TCP_BUILD_MSS_OPTION(*opts); + opts += 1; } +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (seg->flags & TF_SEG_OPTS_TS) { + tcp_build_timestamp_option(pcb, opts); + opts += 3; + } +#endif /* If we don't have a local IP address, we get one by calling ip_route(). */ @@ -542,7 +707,9 @@ tcp_output_segment(struct tcp_seg *seg, ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); } - pcb->rtime = 0; + /* Set retransmission timer running if it is not currently enabled */ + if(pcb->rtime == -1) + pcb->rtime = 0; if (pcb->rttest == 0) { pcb->rttest = tcp_ticks; @@ -570,10 +737,35 @@ tcp_output_segment(struct tcp_seg *seg, #endif TCP_STATS_INC(tcp.xmit); +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ } +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ void tcp_rst(u32_t seqno, u32_t ackno, struct ip_addr *local_ip, struct ip_addr *remote_ip, @@ -586,6 +778,8 @@ tcp_rst(u32_t seqno, u32_t ackno, LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); return; } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); tcphdr = p->payload; tcphdr->src = htons(local_port); @@ -603,13 +797,20 @@ tcp_rst(u32_t seqno, u32_t ackno, IP_PROTO_TCP, p->tot_len); #endif TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); /* Send output with hardcoded TTL since we have no access to the pcb */ ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); pbuf_free(p); LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); } -/* requeue all unacked segments for retransmission */ +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ void tcp_rexmit_rto(struct tcp_pcb *pcb) { @@ -628,7 +829,6 @@ tcp_rexmit_rto(struct tcp_pcb *pcb) /* unacked queue is now empty */ pcb->unacked = NULL; - pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); /* increment number of retransmissions */ ++pcb->nrtx; @@ -639,22 +839,35 @@ tcp_rexmit_rto(struct tcp_pcb *pcb) tcp_output(pcb); } +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retramsmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ void tcp_rexmit(struct tcp_pcb *pcb) { struct tcp_seg *seg; + struct tcp_seg **cur_seg; if (pcb->unacked == NULL) { return; } /* Move the first unacked segment to the unsent queue */ - seg = pcb->unacked->next; - pcb->unacked->next = pcb->unsent; - pcb->unsent = pcb->unacked; - pcb->unacked = seg; + /* Keep the unsent queue sorted. */ + seg = pcb->unacked; + pcb->unacked = seg->next; - pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + cur_seg = &(pcb->unsent); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = *cur_seg; + *cur_seg = seg; ++pcb->nrtx; @@ -662,60 +875,180 @@ tcp_rexmit(struct tcp_pcb *pcb) pcb->rttest = 0; /* Do the actual retransmission. */ - tcp_output(pcb); - + snmp_inc_tcpretranssegs(); + /* No need to call tcp_output: we are always called from tcp_input() + and thus tcp_output directly returns. */ } +/** + * Handle retransmission after three dupacks received + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit_fast(struct tcp_pcb *pcb) +{ + if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: dupacks %"U16_F" (%"U32_F + "), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + + /* Set ssthresh to half of the minimum of the current + * cwnd and the advertised window */ + if (pcb->cwnd > pcb->snd_wnd) + pcb->ssthresh = pcb->snd_wnd / 2; + else + pcb->ssthresh = pcb->cwnd / 2; + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < 2*pcb->mss) { + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: The minimum value for ssthresh %"U16_F + " should be min 2 mss %"U16_F"...\n", + pcb->ssthresh, 2*pcb->mss)); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } +} + + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ void tcp_keepalive(struct tcp_pcb *pcb) { - struct pbuf *p; - struct tcp_hdr *tcphdr; + struct pbuf *p; + struct tcp_hdr *tcphdr; - LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), - ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); - - LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt %"U16_F"\n", tcp_ticks, pcb->tmr, pcb->keep_cnt)); - - p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); - if(p == NULL) { - LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: could not allocate memory for pbuf\n")); - return; - } + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); - tcphdr = p->payload; - tcphdr->src = htons(pcb->local_port); - tcphdr->dest = htons(pcb->remote_port); - tcphdr->seqno = htonl(pcb->snd_nxt - 1); - tcphdr->ackno = htonl(pcb->rcv_nxt); - tcphdr->wnd = htons(pcb->rcv_wnd); - tcphdr->urgp = 0; - TCPH_HDRLEN_SET(tcphdr, 5); - - tcphdr->chksum = 0; + tcphdr = tcp_output_set_header(pcb, p, 0, htonl(pcb->snd_nxt - 1)); + #if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, IP_PROTO_TCP, p->tot_len); + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); #endif TCP_STATS_INC(tcp.xmit); - /* Send output to IP */ + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ pbuf_free(p); - LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", pcb->snd_nxt - 1, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); } -#endif /* LWIP_TCP */ - - +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + u16_t len; + u8_t is_fin; + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: sending ZERO WINDOW probe to %" + U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) + seg = pcb->unsent; + + if(seg == NULL) + return; + + is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); + len = is_fin ? TCP_HLEN : TCP_HLEN + 1; + p = pbuf_alloc(PBUF_IP, len, PBUF_RAM); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + tcphdr = tcp_output_set_header(pcb, p, 0, seg->tcphdr->seqno); + + if (is_fin) { + /* FIN segment, no data */ + TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); + } else { + /* Data segment, copy in one byte from the head of the unacked queue */ + *((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr; + } +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} +#endif /* LWIP_TCP */ diff --git a/packages/net/lwip_tcpip/current/src/core/udp.c b/packages/net/lwip_tcpip/current/src/core/udp.c --- a/packages/net/lwip_tcpip/current/src/core/udp.c +++ b/packages/net/lwip_tcpip/current/src/core/udp.c @@ -3,6 +3,7 @@ * User Datagram Protocol module * */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -38,59 +39,58 @@ /* udp.c * - * The code for the User Datagram Protocol UDP. + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). * */ -#include +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ #include "lwip/opt.h" +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" #include "lwip/def.h" #include "lwip/memp.h" #include "lwip/inet.h" +#include "lwip/inet_chksum.h" #include "lwip/ip_addr.h" #include "lwip/netif.h" -#include "lwip/udp.h" #include "lwip/icmp.h" - #include "lwip/stats.h" - +#include "lwip/snmp.h" #include "arch/perf.h" -#include "lwip/snmp.h" +#include "lwip/dhcp.h" + +#include /* The list of UDP PCBs */ -#if LWIP_UDP -/* was static, but we may want to access this from a socket layer */ -struct udp_pcb *udp_pcbs = NULL; - -static struct udp_pcb *pcb_cache = NULL; - -void -udp_init(void) -{ - udp_pcbs = pcb_cache = NULL; -} +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; /** * Process an incoming UDP datagram. * * Given an incoming UDP datagram (as a chain of pbufs) this function - * finds a corresponding UDP PCB and + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. * - * @param pbuf pbuf to be demultiplexed to a UDP PCB. - * @param netif network interface on which the datagram was received. + * @param p pbuf to be demultiplexed to a UDP PCB. + * @param inp network interface on which the datagram was received. * */ void udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; - struct udp_pcb *pcb; + struct udp_pcb *pcb, *prev; struct udp_pcb *uncon_pcb; struct ip_hdr *iphdr; u16_t src, dest; u8_t local_match; + u8_t broadcast; PERF_START; @@ -98,9 +98,12 @@ udp_input(struct pbuf *p, struct netif * iphdr = p->payload; - if (pbuf_header(p, -((s16_t)(UDP_HLEN + IPH_HL(iphdr) * 4)))) { + /* Check minimum length (IP header + UDP header) + * and move payload pointer to UDP header */ + if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) { /* drop short packets */ - LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); UDP_STATS_INC(udp.lenerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); @@ -108,147 +111,248 @@ udp_input(struct pbuf *p, struct netif * goto end; } - udphdr = (struct udp_hdr *)((u8_t *)p->payload - UDP_HLEN); + udphdr = (struct udp_hdr *)p->payload; + + /* is broadcast packet ? */ + broadcast = ip_addr_isbroadcast(&(iphdr->dest), inp); LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + /* convert src and dest ports to host byte order */ src = ntohs(udphdr->src); dest = ntohs(udphdr->dest); udp_debug_print(udphdr); /* print the UDP source and destination */ - LWIP_DEBUGF(UDP_DEBUG, ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", - ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest), - ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), - ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), - ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src))); - - local_match = 0; - uncon_pcb = NULL; - /* Iterate through the UDP pcb list for a matching pcb */ - for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { - /* print the PCB local and remote address */ - LWIP_DEBUGF(UDP_DEBUG, ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", - ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), - ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, - ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), - ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port)); + LWIP_DEBUGF(UDP_DEBUG, + ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest), + ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), + ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), + ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src))); - /* compare PCB local addr+port to UDP destination addr+port */ - if ((pcb->local_port == dest) && - (ip_addr_isany(&pcb->local_ip) || - ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { - local_match = 1; - if ((uncon_pcb == NULL) && - ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { - /* the first unconnected matching PCB */ - uncon_pcb = pcb; - } +#if LWIP_DHCP + pcb = NULL; + /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by + the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ + if (dest == DHCP_CLIENT_PORT) { + /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ + if (src == DHCP_SERVER_PORT) { + if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { + /* accept the packe if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src */ + if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &(iphdr->src)))) { + pcb = inp->dhcp->pcb; + } + } } - /* compare PCB remote addr+port to UDP source addr+port */ - if ((local_match != 0) && - (pcb->remote_port == src) && - (ip_addr_isany(&pcb->remote_ip) || - ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) { - /* the first fully matching PCB */ - break; + } else +#endif /* LWIP_DHCP */ + { + prev = NULL; + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + local_match = 0; + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, + ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), + ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if ((pcb->local_port == dest) && + ((!broadcast && ip_addr_isany(&pcb->local_ip)) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)) || +#if LWIP_IGMP + ip_addr_ismulticast(&(iphdr->dest)) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && (pcb->so_options & SOF_BROADCAST)))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))) { +#endif /* IP_SOF_BROADCAST_RECV */ + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + prev = pcb; } - } - /* no fully matching pcb found? then look for an unconnected pcb */ - if (pcb == NULL) { - pcb = uncon_pcb; + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } } /* 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)) - { - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE, ("udp_input: calculating checksum\n")); - pbuf_header(p, UDP_HLEN); -#ifdef IPv6 - if (iphdr->nexthdr == IP_PROTO_UDPLITE) { -#else + if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if LWIP_UDPLITE if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { -#endif /* IPv4 */ /* Do the UDP Lite checksum */ #if CHECKSUM_CHECK_UDP - if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), - (struct ip_addr *)&(iphdr->dest), - IP_PROTO_UDPLITE, ntohs(udphdr->len)) != 0) { - LWIP_DEBUGF(UDP_DEBUG | 2, ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); - UDP_STATS_INC(udp.chkerr); - UDP_STATS_INC(udp.drop); - snmp_inc_udpinerrors(); - pbuf_free(p); - goto end; + u16_t chklen = ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } } -#endif - } else { + if (inet_chksum_pseudo_partial(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } +#endif /* CHECKSUM_CHECK_UDP */ + } else +#endif /* LWIP_UDPLITE */ + { #if CHECKSUM_CHECK_UDP 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) { - LWIP_DEBUGF(UDP_DEBUG | 2, ("udp_input: UDP datagram discarded due to failing checksum\n")); - - UDP_STATS_INC(udp.chkerr); - UDP_STATS_INC(udp.drop); - snmp_inc_udpinerrors(); - pbuf_free(p); - goto end; - } + if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDP, p->tot_len) != 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } } -#endif +#endif /* CHECKSUM_CHECK_UDP */ } - pbuf_header(p, -UDP_HLEN); + if(pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } if (pcb != NULL) { snmp_inc_udpindatagrams(); /* callback */ - if (pcb->recv != NULL) - { - pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src); + if (pcb->recv != NULL) { + /* now the recv function is responsible for freeing p */ + pcb->recv(pcb->recv_arg, pcb, p, &iphdr->src, src); + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; } - } else { - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE, ("udp_input: not for us.\n")); + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); +#if LWIP_ICMP /* No match was found, send ICMP destination port unreachable unless - destination address was broadcast/multicast. */ - - if (!ip_addr_isbroadcast(&iphdr->dest, inp) && + destination address was broadcast/multicast. */ + if (!broadcast && !ip_addr_ismulticast(&iphdr->dest)) { - - /* adjust pbuf pointer */ - p->payload = iphdr; - icmp_dest_unreach(p, ICMP_DUR_PORT); + /* move payload pointer back to ip header */ + pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN); + LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr)); + icmp_dest_unreach(p, ICMP_DUR_PORT); } +#endif /* LWIP_ICMP */ UDP_STATS_INC(udp.proterr); UDP_STATS_INC(udp.drop); - snmp_inc_udpnoports(); + snmp_inc_udpnoports(); pbuf_free(p); } } else { pbuf_free(p); } - end: +end: + PERF_STOP("udp_input"); +} - PERF_STOP("udp_input"); +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); } /** * Send data to a specified address using UDP. * * @param pcb UDP PCB used to send the data. - * @param pbuf chain of pbuf's to be sent. + * @param p chain of pbuf's to be sent. * @param dst_ip Destination IP address. * @param dst_port Destination UDP port. * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * * If the PCB already has a remote address association, it will * be restored after the data is sent. * - * @return lwIP error code. - * - ERR_OK. Successful. No error occured. - * - ERR_MEM. Out of memory. - * - ERR_RTE. Could not find route to destination address. + * @return lwIP error code (@see udp_send for possible error codes) * * @see udp_disconnect() udp_send() */ @@ -256,89 +360,99 @@ err_t udp_sendto(struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port) { - err_t err; - /* temporary space for current PCB remote address */ - struct ip_addr pcb_remote_ip; - u16_t pcb_remote_port; - /* remember current remote peer address of PCB */ - pcb_remote_ip.addr = pcb->remote_ip.addr; - pcb_remote_port = pcb->remote_port; - /* copy packet destination address to PCB remote peer address */ - pcb->remote_ip.addr = dst_ip->addr; - pcb->remote_port = dst_port; - /* send to the packet destination address */ - err = udp_send(pcb, p); - /* restore PCB remote peer address */ - pcb->remote_ip.addr = pcb_remote_ip.addr; - pcb->remote_port = pcb_remote_port; - return err; + struct netif *netif; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + + /* find the outgoing network interface for this packet */ +#if LWIP_IGMP + netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); +#else + netif = ip_route(dst_ip); +#endif /* LWIP_IGMP */ + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to 0x%"X32_F"\n", dst_ip->addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); } /** - * Send data using UDP. + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. * * @param pcb UDP PCB used to send the data. - * @param pbuf chain of pbuf's to be sent. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. * - * @return lwIP error code. - * - ERR_OK. Successful. No error occured. - * - ERR_MEM. Out of memory. - * - ERR_RTE. Could not find route to destination address. + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. * - * @see udp_disconnect() udp_sendto() + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() */ err_t -udp_send(struct udp_pcb *pcb, struct pbuf *p) +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif) { struct udp_hdr *udphdr; - struct netif *netif; struct ip_addr *src_ip; err_t err; struct pbuf *q; /* q will be sent down the stack */ - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, ("udp_send\n")); +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ /* if the PCB is not yet bound to a port, bind it here */ if (pcb->local_port == 0) { - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 2, ("udp_send: not yet bound to a port, binding now\n")); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); if (err != ERR_OK) { - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 2, ("udp_send: forced port bind failed\n")); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); return err; } } - /* find the outgoing network interface for this packet */ - netif = ip_route(&(pcb->remote_ip)); - /* no outgoing network interface could be found? */ - if (netif == NULL) { - LWIP_DEBUGF(UDP_DEBUG | 1, ("udp_send: No route to 0x%"X32_F"\n", pcb->remote_ip.addr)); - UDP_STATS_INC(udp.rterr); - return ERR_RTE; - } /* not enough space to add an UDP header to first pbuf in given p chain? */ if (pbuf_header(p, UDP_HLEN)) { - /* allocate header in a seperate new pbuf */ + /* allocate header in a separate new pbuf */ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 2, ("udp_send: could not allocate header\n")); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); return ERR_MEM; } /* chain header q in front of given pbuf p */ pbuf_chain(q, p); - /* { first pbuf q points to header pbuf } */ - LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); - /* adding a header within p succeeded */ + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); } else { + /* adding space for header within p succeeded */ /* first pbuf q equals given pbuf */ q = p; LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); } - /* { q now represents the packet to be sent } */ + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ udphdr = q->payload; udphdr->src = htons(pcb->local_port); - udphdr->dest = htons(pcb->remote_port); + udphdr->dest = htons(dst_port); /* in UDP, 0 checksum means 'no checksum' */ udphdr->chksum = 0x0000; @@ -347,56 +461,95 @@ udp_send(struct udp_pcb *pcb, struct pbu /* use outgoing network interface IP address as source address */ src_ip = &(netif->ip_addr); } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + return ERR_VAL; + } /* use UDP PCB local IP address as source address */ src_ip = &(pcb->local_ip); } LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); +#if LWIP_UDPLITE /* UDP Lite protocol? */ if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); /* set UDP message length in UDP header */ - udphdr->len = htons(pcb->chksum_len); + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = htons(chklen_hdr); /* calculate checksum */ #if CHECKSUM_GEN_UDP - udphdr->chksum = inet_chksum_pseudo(q, src_ip, &(pcb->remote_ip), - IP_PROTO_UDP, pcb->chksum_len); + udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, + IP_PROTO_UDPLITE, q->tot_len, chklen); /* chksum zero must become 0xffff, as zero means 'no checksum' */ - if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; -#else - udphdr->chksum = 0x0000; -#endif + if (udphdr->chksum == 0x0000) + udphdr->chksum = 0xffff; +#endif /* CHECKSUM_CHECK_UDP */ /* output to IP */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); - err = ip_output_if (q, src_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); - /* UDP */ - } else { +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); udphdr->len = htons(q->tot_len); /* calculate checksum */ #if CHECKSUM_GEN_UDP if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { - udphdr->chksum = inet_chksum_pseudo(q, src_ip, &pcb->remote_ip, IP_PROTO_UDP, q->tot_len); + udphdr->chksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len); /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; } -#else - udphdr->chksum = 0x0000; -#endif +#endif /* CHECKSUM_CHECK_UDP */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); /* output to IP */ - err = ip_output_if(q, src_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ } /* TODO: must this be increased even if error occured? */ snmp_inc_udpoutdatagrams(); - /* did we chain a seperate header pbuf earlier? */ + /* did we chain a separate header pbuf earlier? */ if (q != p) { /* free the header pbuf */ - pbuf_free(q); q = NULL; - /* { p is still referenced by the caller, and will live on } */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ } UDP_STATS_INC(udp.xmit); @@ -409,7 +562,11 @@ udp_send(struct udp_pcb *pcb, struct pbu * @param pcb UDP PCB to be bound with a local address ipaddr and port. * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to * bind to all local interfaces. - * @param port local UDP port to bind with. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. * * @return lwIP error code. * - ERR_OK. Successful. No error occured. @@ -424,9 +581,9 @@ udp_bind(struct udp_pcb *pcb, struct ip_ struct udp_pcb *ipcb; u8_t rebind; - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, ("udp_bind(ipaddr = ")); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); ip_addr_debug_print(UDP_DEBUG, ipaddr); - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, (", port = %"U16_F")\n", port)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); rebind = 0; /* Check for double bind and rebind of the same pcb */ @@ -439,26 +596,28 @@ udp_bind(struct udp_pcb *pcb, struct ip_ rebind = 1; } -/* this code does not allow upper layer to share a UDP port for - listening to broadcast or multicast traffic (See SO_REUSE_ADDR and - SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR - combine with implementation of UDP PCB flags. Leon Woestenberg. */ + /* this code does not allow upper layer to share a UDP port for + listening to broadcast or multicast traffic (See SO_REUSE_ADDR and + SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR + combine with implementation of UDP PCB flags. Leon Woestenberg. */ #ifdef LWIP_UDP_TODO /* port matches that of PCB in list? */ - else if ((ipcb->local_port == port) && - /* IP address matches, or one is IP_ADDR_ANY? */ - (ip_addr_isany(&(ipcb->local_ip)) || - ip_addr_isany(ipaddr) || - ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { - /* other PCB already binds to this local IP and port */ - LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); - return ERR_USE; - } + else + if ((ipcb->local_port == port) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ip_addr_isany(&(ipcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } #endif - } ip_addr_set(&pcb->local_ip, ipaddr); + /* no port specified? */ if (port == 0) { #ifndef UDP_LOCAL_PORT_RANGE_START @@ -469,9 +628,12 @@ udp_bind(struct udp_pcb *pcb, struct ip_ ipcb = udp_pcbs; while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { if (ipcb->local_port == port) { + /* port is already used by another udp_pcb */ port++; + /* restart scanning all udp pcbs */ ipcb = udp_pcbs; } else + /* go on with next udp pcb */ ipcb = ipcb->next; } if (ipcb != NULL) { @@ -481,17 +643,19 @@ udp_bind(struct udp_pcb *pcb, struct ip_ } } pcb->local_port = port; + snmp_insert_udpidx_tree(pcb); /* pcb not active yet? */ if (rebind == 0) { /* place the PCB on the active list if not already there */ pcb->next = udp_pcbs; udp_pcbs = pcb; } - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | DBG_STATE, ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", - (u16_t)(ntohl(pcb->local_ip.addr) >> 24 & 0xff), - (u16_t)(ntohl(pcb->local_ip.addr) >> 16 & 0xff), - (u16_t)(ntohl(pcb->local_ip.addr) >> 8 & 0xff), - (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), pcb->local_port)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", + (u16_t)((ntohl(pcb->local_ip.addr) >> 24) & 0xff), + (u16_t)((ntohl(pcb->local_ip.addr) >> 16) & 0xff), + (u16_t)((ntohl(pcb->local_ip.addr) >> 8) & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), pcb->local_port)); return ERR_OK; } /** @@ -505,6 +669,10 @@ udp_bind(struct udp_pcb *pcb, struct ip_ * * @return lwIP error code * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * * @see udp_disconnect() */ err_t @@ -529,7 +697,7 @@ udp_connect(struct udp_pcb *pcb, struct if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr)); - UDP_STATS_INC(udp.rterr); + UDP_STATS_INC(udp.rterr); return ERR_RTE; } /** TODO: this will bind the udp pcb locally, to the interface which @@ -540,14 +708,15 @@ udp_connect(struct udp_pcb *pcb, struct pcb->local_ip.addr = 0; } #endif - LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | DBG_STATE, ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", - (u16_t)(ntohl(pcb->remote_ip.addr) >> 24 & 0xff), - (u16_t)(ntohl(pcb->remote_ip.addr) >> 16 & 0xff), - (u16_t)(ntohl(pcb->remote_ip.addr) >> 8 & 0xff), - (u16_t)(ntohl(pcb->remote_ip.addr) & 0xff), pcb->remote_port)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", + (u16_t)((ntohl(pcb->remote_ip.addr) >> 24) & 0xff), + (u16_t)((ntohl(pcb->remote_ip.addr) >> 16) & 0xff), + (u16_t)((ntohl(pcb->remote_ip.addr) >> 8) & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) & 0xff), pcb->remote_port)); /* Insert UDP PCB into the list of active UDP PCBs. */ - for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { if (pcb == ipcb) { /* already on the list, just return */ return ERR_OK; @@ -559,6 +728,11 @@ udp_connect(struct udp_pcb *pcb, struct return ERR_OK; } +/** + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ void udp_disconnect(struct udp_pcb *pcb) { @@ -569,16 +743,26 @@ udp_disconnect(struct udp_pcb *pcb) pcb->flags &= ~UDP_FLAGS_CONNECTED; } +/** + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for wich to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ void udp_recv(struct udp_pcb *pcb, - void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, - struct ip_addr *addr, u16_t port), - void *recv_arg) + void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port), + void *recv_arg) { /* remember recv() callback and user data */ pcb->recv = recv; pcb->recv_arg = recv_arg; } + /** * Remove an UDP PCB. * @@ -591,20 +775,24 @@ void udp_remove(struct udp_pcb *pcb) { struct udp_pcb *pcb2; + + snmp_delete_udpidx_tree(pcb); /* pcb to be removed is first in list? */ if (udp_pcbs == pcb) { /* make list start at 2nd pcb */ udp_pcbs = udp_pcbs->next; - /* pcb not 1st in list */ - } else for(pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { - /* find pcb in udp_pcbs list */ - if (pcb2->next != NULL && pcb2->next == pcb) { - /* remove pcb from list */ - pcb2->next = pcb->next; + /* pcb not 1st in list */ + } else + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } } - } memp_free(MEMP_UDP_PCB, pcb); } + /** * Create a UDP PCB. * @@ -614,42 +802,40 @@ udp_remove(struct udp_pcb *pcb) * @see udp_remove() */ struct udp_pcb * -udp_new(void) { +udp_new(void) +{ struct udp_pcb *pcb; pcb = memp_malloc(MEMP_UDP_PCB); /* could allocate UDP PCB? */ if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ /* initialize PCB to all zeroes */ memset(pcb, 0, sizeof(struct udp_pcb)); pcb->ttl = UDP_TTL; } - - return pcb; } #if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ void udp_debug_print(struct udp_hdr *udphdr) { LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", - ntohs(udphdr->src), ntohs(udphdr->dest))); + ntohs(udphdr->src), ntohs(udphdr->dest))); LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", - ntohs(udphdr->len), ntohs(udphdr->chksum))); + ntohs(udphdr->len), ntohs(udphdr->chksum))); LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); } #endif /* UDP_DEBUG */ #endif /* LWIP_UDP */ - - - - - - - - - diff --git a/packages/net/lwip_tcpip/current/src/ecos/ppp.c b/packages/net/lwip_tcpip/current/src/ecos/ppp.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/ecos/ppp.c @@ -0,0 +1,75 @@ +//========================================================================== +// +// ppp.c +// +// lwIP PPP support. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2009-05-14 +// Purpose: +// Description: lwIP PPP support. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include + +#include +#include + +void ppp_trace(int level, const char *format, ...) +{ + va_list args; + + (void)level; + va_start(args, format); + diag_vprintf(format, args); + va_end(args); +} + +void ppp_dump_record(void *data, int len) +{ + cyg_uint8 *c = data; + + diag_printf("PPPDUMP:"); + while (len--) + diag_printf("%02x", *c++); + diag_printf("\n"); +} diff --git a/packages/net/lwip_tcpip/current/src/ecos/sequential.c b/packages/net/lwip_tcpip/current/src/ecos/sequential.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/ecos/sequential.c @@ -0,0 +1,317 @@ +//========================================================================== +// +// sequential.c +// +// lwIP sequential mode (multi-threaded) support. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2008-12-02 +// Purpose: +// Description: lwIP sequential mode (multi-threaded) support. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include + +#include "lwip.h" +#include "lwip/opt.h" +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/init.h" +#include "lwip/tcp.h" +#include "lwip/ip_frag.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +#include "netif/loopif.h" +#include "netif/slipif.h" +#include "netif/etharp.h" +#include "netif/ppp/ppp.h" + +#include +#include + +#ifdef CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT +#include // HAL_CTRLC_CHECK +#endif + +#ifdef CYGPKG_LWIP_ETH +#include +#include +#endif + +#if LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE) +static struct netif loopif; +#endif + +#if LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE) +static struct netif slipif; +#endif + +#ifdef CYGPKG_LWIP_ETH + +// Ethernet driver table +CYG_HAL_TABLE_BEGIN(__NETDEVTAB__, netdev); +CYG_HAL_TABLE_END(__NETDEVTAB_END__, netdev); + +static sys_thread_t eth_thread_handle; +static cyg_sem_t eth_thread_sem; + +static void eth_thread(void *data); + +#endif + +#if (LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE)) || \ + (LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE)) + +// +// Sets an IP address. +// +static void +set_ip_addr(struct ip_addr *addr, u8_t a, u8_t b, u8_t c, u8_t d) +{ + IP4_ADDR(addr, a, b, c, d); +} + +#endif // (LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE)) || \ + (LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE)) + +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + +static void +show_netif_config(struct netif *netif) +{ + diag_printf( + "%c%c%d: IP: %d.%d.%d.%d Submask: %d.%d.%d.%d Gateway: %d.%d.%d.%d\n", + netif->name[0], netif->name[1], netif->num, + ip4_addr1(&netif->ip_addr), ip4_addr2(&netif->ip_addr), + ip4_addr3(&netif->ip_addr), ip4_addr4(&netif->ip_addr), + ip4_addr1(&netif->netmask), ip4_addr2(&netif->netmask), + ip4_addr3(&netif->netmask), ip4_addr4(&netif->netmask), + ip4_addr1(&netif->gw), ip4_addr2(&netif->gw), + ip4_addr3(&netif->gw), ip4_addr4(&netif->gw) + ); +} + +static void +netif_status_callback(struct netif *netif) +{ + if (netif_is_up(netif)) + show_netif_config(netif); +} + +#endif // CYGFUN_LWIP_SHOW_NETIF_CONFIG + + +// +// Called when initialization of the tcpip thread is done. +// +static void +tcpip_init_done(void *arg) +{ + cyg_sem_t *sem = arg; + +#if LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE) + { + struct ip_addr addr, netmask, gateway; + + // Setup default loopback device instance + set_ip_addr(&addr, CYGDAT_LWIP_LOOPIF_ADDR); + set_ip_addr(&netmask, CYGDAT_LWIP_LOOPIF_NETMASK); + set_ip_addr(&gateway, CYGDAT_LWIP_LOOPIF_GATEWAY); + netif_add(&loopif, &addr, &netmask, &gateway, NULL, + loopif_init, tcpip_input); +#ifdef CYGDAT_LWIP_LOOPIF_DEFAULT + netif_set_default(&loopif); +#endif +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + netif_set_status_callback(&loopif, netif_status_callback); +#endif + netif_set_up(&loopif); + } +#endif + +#if LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE) + { + struct ip_addr addr, netmask, gateway; + + // Setup default SLIP device instance + set_ip_addr(&addr, CYGDAT_LWIP_SLIPIF_ADDR); + set_ip_addr(&netmask, CYGDAT_LWIP_SLIPIF_NETMASK); + set_ip_addr(&gateway, CYGDAT_LWIP_SLIPIF_GATEWAY); + netif_add(&slipif, &addr, &netmask, &gateway, NULL, + slipif_init, tcpip_input); +#ifdef CYGDAT_LWIP_SLIPIF_DEFAULT + netif_set_default(&slipif); +#endif +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + netif_set_status_callback(&slipif, netif_status_callback); +#endif + netif_set_up(&slipif); + } +#endif + +#if PPP_SUPPORT + { + // Setup PPP instance + } +#endif + +#ifdef CYGPKG_LWIP_ETH + { + cyg_netdevtab_entry_t *t; + + // Initialize ethernet delivery thread + cyg_semaphore_init(ð_thread_sem, 0); + eth_thread_handle = sys_thread_new(ETH_THREAD_NAME, + eth_thread, + NULL, + ETH_THREAD_STACKSIZE, + ETH_THREAD_PRIO); + + // Initialize network devices + for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { + if (t->init(t)) { + t->status = CYG_NETDEVTAB_STATUS_AVAIL; +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + { + struct eth_drv_sc* sc = (struct eth_drv_sc*) (t->device_instance); + struct netif *netif = &sc->sc_arpcom.ac_if; + netif_set_status_callback(netif, netif_status_callback); + } +#endif + } else { + // Device not [currently] available + t->status = 0; + } + } + } +#endif + + // Notify initialization thread + cyg_semaphore_post(sem); +} + +// +// Initialize lwIP for simple (single-threaded) mode. +// +void +cyg_lwip_sequential_init(void) +{ + static int initialized; + cyg_sem_t sem; + + // Only initialize once + if (initialized) + return; + initialized = 1; + + // Initialize the lwIP stack + cyg_semaphore_init(&sem, 0); + tcpip_init(tcpip_init_done, (void *) &sem); + + // Wait until initialized + cyg_semaphore_wait(&sem); + cyg_semaphore_destroy(&sem); +} + +#ifdef CYGPKG_LWIP_ETH + +static void +eth_thread(void *data) +{ + cyg_netdevtab_entry_t *t; + + while (1) { + cyg_semaphore_wait(ð_thread_sem); + + for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { + struct eth_drv_sc *sc = (struct eth_drv_sc *)t->device_instance; + if (sc->state & ETH_DRV_NEEDS_DELIVERY) { +#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) + cyg_bool was_ctrlc_int; +#endif + sc->state &= ~ETH_DRV_NEEDS_DELIVERY; +#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) + was_ctrlc_int = HAL_CTRLC_CHECK(sc->funs->int_vector(sc), + (int) sc); + if (!was_ctrlc_int) // Fall through and run normal code +#endif + sc->funs->deliver(sc); + } + } + } +} + +// +// Called from the network driver to indicate a new netif. +// +void +lwip_eth_drv_new(struct netif *netif) +{ +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + netif_set_status_callback(netif, netif_status_callback); +#endif +} + +// +// Called from network driver to indicate interrupt. +// +void +lwip_eth_drv_dsr(void) +{ + cyg_semaphore_post(ð_thread_sem); +} + +// +// Called from the network driver when a packet was received. +// +err_t +lwip_eth_drv_input(struct pbuf *p, struct netif *netif) +{ + return tcpip_input(p, netif); +} + +#endif // CYGPKG_LWIP_ETH diff --git a/packages/net/lwip_tcpip/current/src/ecos/simple.c b/packages/net/lwip_tcpip/current/src/ecos/simple.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/ecos/simple.c @@ -0,0 +1,416 @@ +//========================================================================== +// +// simple.c +// +// lwIP simple mode (single-threaded) support. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2008-12-08 +// Purpose: +// Description: lwIP simple mode (single-threaded) support. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include + +#include "lwip.h" +#include "lwip/opt.h" +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/init.h" +#include "lwip/tcp.h" +#include "lwip/ip_frag.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/memp.h" +#include "lwip/netif.h" + +#include "netif/loopif.h" +#include "netif/slipif.h" +#include "netif/etharp.h" +#include "netif/ppp/ppp.h" +#include "netif/ppp/chat.h" + +#include +#include + +#ifdef CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT +#include // HAL_CTRLC_CHECK +#endif + +#ifdef CYGPKG_LWIP_ETH +#include +#include +#endif + +#if LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE) +static struct netif loopif; +#endif + +#if LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE) +static struct netif slipif; +#endif + +#ifdef CYGPKG_LWIP_ETH + +// Ethernet driver table +CYG_HAL_TABLE_BEGIN(__NETDEVTAB__, netdev); +CYG_HAL_TABLE_END(__NETDEVTAB_END__, netdev); + +// Received packet +struct recv_packet { + struct netif *netif; + struct pbuf *p; +}; + +#define MAX_RECV_PACKETS (PBUF_POOL_SIZE + 1) +static struct recv_packet recv_packet[MAX_RECV_PACKETS]; +static int recv_packet_read; +static int recv_packet_write; +static int recv_packet_count; + +#endif // CYGPKG_LWIP_ETH + +// lwIP interval timer +struct lwip_timer { + cyg_uint32 time; // Time counter in ms + cyg_uint32 interval; // Interval in ms + void (*timer_func)(void); // lwIP timer to call +}; + +// List of lwIP interval timers +static struct lwip_timer lwip_timers[] = { +#if LWIP_TCP + { 0, TCP_TMR_INTERVAL, tcp_tmr }, +#endif +#if LWIP_ARP + { 0, ARP_TMR_INTERVAL, etharp_tmr }, +#endif +#if LWIP_DHCP + { 0, DHCP_FINE_TIMER_MSECS, dhcp_fine_tmr }, + { 0, DHCP_COARSE_TIMER_MSECS, dhcp_coarse_tmr }, +#endif +#if IP_REASSEMBLY + { 0, IP_TMR_INTERVAL, ip_reass_tmr }, +#endif +#if LWIP_AUTOIP + { 0, AUTOIP_TMR_INTERVAL, autoip_tmr }, +#endif +#if LWIP_IGMP + { 0, IGMP_TMR_INTERVAL, igmp_tmr }, +#endif +#if LWIP_DNS + { 0, DNS_TMR_INTERVAL, dns_tmr }, +#endif +#if PPP_SUPPORT + { 0, PPP_TMR_INTERVAL, ppp_tmr }, + { 0, CHAT_TMR_INTERVAL, chat_tmr }, +#endif +}; + +#define NUM_LWIP_TIMERS (sizeof(lwip_timers) / sizeof(lwip_timers[0])) + +#define TICKS_TO_MS(_ticks_) \ + ((_ticks_) * (CYGNUM_HAL_RTC_NUMERATOR / CYGNUM_HAL_RTC_DENOMINATOR / 1000000LL)) + +static cyg_tick_count_t last_ticks; + + +#if (LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE)) || \ + (LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE)) + +// +// Sets an IP address. +// +static void +set_ip_addr(struct ip_addr *addr, u8_t a, u8_t b, u8_t c, u8_t d) +{ + IP4_ADDR(addr, a, b, c, d); +} + +// +// Process an incoming IP or ethernet packet. +// +static err_t +simple_input(struct pbuf *p, struct netif *netif) +{ +#if LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + ethernet_input(p, netif); + } else +#endif + { + ip_input(p, netif); + } + + return ERR_OK; +} + +#endif // (LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE)) || \ + (LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE)) + +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + +static void +show_netif_config(struct netif *netif) +{ + diag_printf( + "%c%c%d: IP: %d.%d.%d.%d Submask: %d.%d.%d.%d Gateway: %d.%d.%d.%d\n", + netif->name[0], netif->name[1], netif->num, + ip4_addr1(&netif->ip_addr), ip4_addr2(&netif->ip_addr), + ip4_addr3(&netif->ip_addr), ip4_addr4(&netif->ip_addr), + ip4_addr1(&netif->netmask), ip4_addr2(&netif->netmask), + ip4_addr3(&netif->netmask), ip4_addr4(&netif->netmask), + ip4_addr1(&netif->gw), ip4_addr2(&netif->gw), + ip4_addr3(&netif->gw), ip4_addr4(&netif->gw) + ); +} + +static void +netif_status_callback(struct netif *netif) +{ + if (netif_is_up(netif)) + show_netif_config(netif); +} + +#endif // CYGFUN_LWIP_SHOW_NETIF_CONFIG + + +// +// Initialize lwIP for simple (single-threaded) mode. +// +void +cyg_lwip_simple_init(void) +{ + static int initialized; + + // Only initialize once + if (initialized) + return; + initialized = 1; + + // Initialize the lwIP stack + lwip_init(); + +#if LWIP_HAVE_LOOPIF && defined(CYGIMP_LWIP_LOOPIF_INSTANCE) + { + struct ip_addr addr, netmask, gateway; + + // Setup default loopback device instance + set_ip_addr(&addr, CYGDAT_LWIP_LOOPIF_ADDR); + set_ip_addr(&netmask, CYGDAT_LWIP_LOOPIF_NETMASK); + set_ip_addr(&gateway, CYGDAT_LWIP_LOOPIF_GATEWAY); + netif_add(&loopif, &addr, &netmask, &gateway, NULL, + loopif_init, simple_input); +#ifdef CYGDAT_LWIP_LOOPIF_DEFAULT + netif_set_default(&loopif); +#endif +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + netif_set_status_callback(&loopif, netif_status_callback); +#endif + netif_set_up(&loopif); + } +#endif + +#if LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE) + { + struct ip_addr addr, netmask, gateway; + + // Setup default SLIP device instance + set_ip_addr(&addr, CYGDAT_LWIP_SLIPIF_ADDR); + set_ip_addr(&netmask, CYGDAT_LWIP_SLIPIF_NETMASK); + set_ip_addr(&gateway, CYGDAT_LWIP_SLIPIF_GATEWAY); + netif_add(&slipif, &addr, &netmask, &gateway, NULL, + slipif_init, simple_input); +#ifdef CYGDAT_LWIP_SLIPIF_DEFAULT + netif_set_default(&slipif); +#endif +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + netif_set_status_callback(&slipif, netif_status_callback); +#endif + netif_set_up(&slipif); + } +#endif + +#if PPP_SUPPORT + { + // Setup PPP instance + } +#endif + +#ifdef CYGPKG_LWIP_ETH + { + cyg_netdevtab_entry_t *t; + + // Initialize network devices + for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { + if (t->init(t)) { + t->status = CYG_NETDEVTAB_STATUS_AVAIL; + } else { + // Device not [currently] available + t->status = 0; + } + } + } +#endif + + cyg_lwip_simple_restart(); +} + +// +// Restarts lwIP when not polled for a longer period. +// +void +cyg_lwip_simple_restart(void) +{ + last_ticks = cyg_current_time(); +} + +// +// Polls the lwIP stack. +// +void +cyg_lwip_simple_poll(void) +{ + cyg_tick_count_t ticks; + cyg_uint32 delta; + int i; + +#if LWIP_HAVE_SLIPIF && defined(CYGIMP_LWIP_SLIPIF_INSTANCE) + // Poll SLIP device + slipif_poll(&slipif); +#endif + +#if PPP_SUPPORT + // Poll PPP device +#endif + +#ifdef CYGPKG_LWIP_ETH + { + cyg_netdevtab_entry_t *t; + + // Poll ethernet devices + for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { + struct eth_drv_sc *sc = (struct eth_drv_sc *)t->device_instance; + if (sc->state & ETH_DRV_NEEDS_DELIVERY) { +#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) + cyg_bool was_ctrlc_int; +#endif + sc->state &= ~ETH_DRV_NEEDS_DELIVERY; +#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) + was_ctrlc_int = HAL_CTRLC_CHECK(sc->funs->int_vector(sc), + (int) sc); + if (!was_ctrlc_int) // Fall through and run normal code +#endif + sc->funs->deliver(sc); + } + } + + // Deliver received packets + while (recv_packet_count > 0) { + ethernet_input(recv_packet[recv_packet_read].p, + recv_packet[recv_packet_read].netif); + recv_packet_read = (recv_packet_read + 1) % MAX_RECV_PACKETS; + recv_packet_count--; + } + } +#endif + + // Process timers + ticks = cyg_current_time(); + delta = TICKS_TO_MS(ticks - last_ticks); + last_ticks = ticks; + + // The following check 'delta > 0' rejects a potential wrap-around in the + // system ticks counter. + if (delta > 0) { + for (i = 0; i < NUM_LWIP_TIMERS; i++) { + lwip_timers[i].time += delta; + if (lwip_timers[i].time >= lwip_timers[i].interval) { + lwip_timers[i].timer_func(); + lwip_timers[i].time -= lwip_timers[i].interval; + } + } + } +} + +#ifdef CYGPKG_LWIP_ETH + +// +// Called from the network driver to indicate a new netif. +// +void +lwip_eth_drv_new(struct netif *netif) +{ +#ifdef CYGFUN_LWIP_SHOW_NETIF_CONFIG + netif_set_status_callback(netif, netif_status_callback); +#endif +} + +// +// Called from network driver to indicate interrupt. +// +void +lwip_eth_drv_dsr(void) +{ +} + +// +// Called from the network driver when a packet was received. +// +err_t +lwip_eth_drv_input(struct pbuf *p, struct netif *netif) +{ + if (recv_packet_count >= MAX_RECV_PACKETS) + return ERR_MEM; + + recv_packet[recv_packet_write].netif = netif; + recv_packet[recv_packet_write].p = p; + recv_packet_write = (recv_packet_write + 1) % MAX_RECV_PACKETS; + recv_packet_count++; + + return ERR_OK; +} + +#endif // CYGPKG_LWIP_ETH diff --git a/packages/net/lwip_tcpip/current/src/ecos/sio.c b/packages/net/lwip_tcpip/current/src/ecos/sio.c --- a/packages/net/lwip_tcpip/current/src/ecos/sio.c +++ b/packages/net/lwip_tcpip/current/src/ecos/sio.c @@ -1,112 +1,238 @@ //========================================================================== -// ####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2004 Free Software Foundation, Inc. +// +// sio.c +// +// Serial operations for SLIP, PPP etc. // -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later -// version. +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation // -// eCos is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// You should have received a copy of the GNU General Public License -// along with eCos; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. // -// As a special exception, if other files instantiate templates or use -// macros or inline functions from this file, or you compile this file -// and link it with other works to produce a work based on this file, -// this file does not by itself cause the resulting work to be covered by -// the GNU General Public License. However the source code for this file -// must still be made available in accordance with section (3) of the GNU -// General Public License v2. +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### // -// This exception does not invalidate any other reasons why a work based -// on this file might be covered by the GNU General Public License. -// ------------------------------------------- -// ####ECOSGPLCOPYRIGHTEND#### +// Author(s): Simon Kallweit +// Contributors: +// Date: 2008-12-01 +// Purpose: +// Description: Serial operations for SLIP, PPP etc. +// +//####DESCRIPTIONEND#### +// //========================================================================== -/* Serial operations for SLIP */ +#include +#include "lwip.h" #include "lwip/opt.h" #include "lwip/def.h" #include "lwip/sys.h" #include "lwip/netif.h" +#include "lwip/sio.h" +#include #include +#include #include +#include #include -static cyg_io_handle_t ser; -static int len; +/* + * Opens a serial device for communication. + */ +sio_fd_t sio_open(u8_t devnum) +{ + Cyg_ErrNo ret; + cyg_io_handle_t handle; + char *dev; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_open(devnum=%d)\n", devnum); +#endif -void -sio_send(char c,void * dev) -{ - len = 1; - cyg_io_write(*(cyg_io_handle_t*)dev, &c, &len); + switch (devnum) { +#ifdef CYGPKG_LWIP_SLIP + case SIO_DEV_SLIPIF: + dev = CYGDAT_LWIP_SLIPIF_DEV; + break; +#endif +#ifdef CYGFUN_LWIP_PPPOS_SUPPORT + case SIO_DEV_PPPOS: + dev = CYGDAT_LWIP_PPPOS_DEV; + break; +#endif + default: + // Unknown serial io device + return NULL; + break; + } + + ret = cyg_io_lookup(dev, &handle); + if (ret != ENOERR) + return NULL; + + return handle; } -char -sio_recv(void * dev) +/* + * Sends a single character to the serial device. + */ +void sio_send(u8_t c, sio_fd_t fd) { - char c; - len = 1; - cyg_io_read(*(cyg_io_handle_t *)dev, &c, &len); - return c; + cyg_uint32 len = 1; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_send(c=0x%02x,fd=%p)\n", c, fd); +#endif + + cyg_io_write((cyg_io_handle_t) fd, &c, &len); +} + +/* + * Receives a single character from the serial device. + */ +u8_t sio_recv(sio_fd_t fd) +{ + cyg_uint32 len = 1; + char c; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_recv(fd=%p)\n", fd); +#endif + + cyg_io_read((cyg_io_handle_t) fd, &c, &len); + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_recv: %02X\n", (cyg_uint8) c); +#endif + + return c; } -int -sio_write(void *dev, char *b, int size) +/* + * Reads from the serial device. + */ +u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len) { - int len = size; - cyg_io_write(*(cyg_io_handle_t*)dev, b, &len); - return len; -} - -int -sio_read(void *dev, char *b, int size) -{ - int len = size; - cyg_io_read(*(cyg_io_handle_t*)dev, b, &len); - - return len; + Cyg_ErrNo ret; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_read(fd=%p,data=%p,len=%lu:)\n", fd, data, len); +#endif + + ret = cyg_io_read((cyg_io_handle_t) fd, data, (cyg_uint32 *) &len); + if (ret != ENOERR) + return 0; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_read: "); + diag_dump_buf(data, len); +#endif + + return len; } -void * -sio_open(int devnum) +/* + * Tries to read from the serial device. Same as sio_read but returns + * immediately if no data is available and never blocks. + */ +u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len) { - int res; - cyg_uint32 nb = 0, len = 4; - -#if LWIP_SLIP - #define SIODEV SLIP_DEV -#elif PPP_SUPPORT - #define SIODEV PPP_DEV + Cyg_ErrNo ret; + cyg_serial_buf_info_t info; + cyg_uint32 l; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_tryread(fd=%p,data=%p,len=%lu:)\n", fd, data, len); #endif - res = cyg_io_lookup(SIODEV, &ser); - if (res != ENOERR) - diag_printf("Cannot open %s\n", SIODEV); - res = cyg_io_set_config(ser, CYG_IO_SET_CONFIG_READ_BLOCKING, &nb, &len); - res = cyg_io_set_config(ser, CYG_IO_SET_CONFIG_WRITE_BLOCKING, &nb, &len); - len = 4; - nb = 0xFFFFFFFF; - res = cyg_io_set_config(ser, CYG_IO_SET_CONFIG_SERIAL_FLOW_CONTROL_METHOD, &nb, &len); - if (res != ENOERR) - diag_printf("set_config flow_control returned an error\n"); - return &ser; + // Check how many bytes there are to read + l = sizeof(info); + ret = cyg_io_get_config((cyg_io_handle_t) fd, CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO, &info, &l); + if (ret != ENOERR) + return 0; + l = info.rx_count; + if (l < 1) + return 0; + if (l > len) + l = len; + + ret = cyg_io_read((cyg_io_handle_t) fd, data, &l); + if (ret != ENOERR) + return 0; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_tryread: "); + diag_dump_buf(data, len); +#endif + + return l; } -void -sio_read_abort(void * dev) +/* + * Writes to the serial device. + */ +u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len) { - diag_printf("Abort called\n"); + Cyg_ErrNo ret; + cyg_uint32 count = 0; + cyg_uint32 chunk; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_write(fd=%p,data=%p,len=%lu:)\n", fd, data, len); + diag_printf("sio_write: "); + diag_dump_buf(data, len); +#endif + + while (count < len) { + chunk = len - count; + ret = cyg_io_write((cyg_io_handle_t) fd, data, &chunk); + if (ret != ENOERR) + break; + data += chunk; + count += chunk; + } + + return count; } + +/* + * Aborts a blocking sio_read() call. + */ +void sio_read_abort(sio_fd_t fd) +{ + cyg_uint32 l = 0; + +#ifdef CYGDBG_LWIP_DEBUG_SIO + diag_printf("sio_read_abort(fd=%p)\n", fd); +#endif + + cyg_io_set_config((cyg_io_handle_t) fd, CYG_IO_GET_CONFIG_SERIAL_ABORT, NULL, &l); +} diff --git a/packages/net/lwip_tcpip/current/src/ecos/sys_arch.c b/packages/net/lwip_tcpip/current/src/ecos/sys_arch.c --- a/packages/net/lwip_tcpip/current/src/ecos/sys_arch.c +++ b/packages/net/lwip_tcpip/current/src/ecos/sys_arch.c @@ -1,277 +1,490 @@ //========================================================================== -// ####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2004 Free Software Foundation, Inc. +// +// sys_arch.c +// +// lwIP system architecture support. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2009 Free Software Foundation // -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later -// version. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. // -// eCos is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// You should have received a copy of the GNU General Public License -// along with eCos; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // -// As a special exception, if other files instantiate templates or use -// macros or inline functions from this file, or you compile this file -// and link it with other works to produce a work based on this file, -// this file does not by itself cause the resulting work to be covered by -// the GNU General Public License. However the source code for this file -// must still be made available in accordance with section (3) of the GNU -// General Public License v2. +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### // -// This exception does not invalidate any other reasons why a work based -// on this file might be covered by the GNU General Public License. -// ------------------------------------------- -// ####ECOSGPLCOPYRIGHTEND#### +// Author(s): Simon Kallweit +// Contributors: +// Date: 2008-12-02 +// Purpose: +// Description: Provides the system architecture support for lwIP. +// +//####DESCRIPTIONEND#### +// //========================================================================== -// Author: Jani Monoses -// Contributors: Claudio Leonel Salvadori -// + +#include -/* - * This file implements the eCos specific sys_arch functions used by lwIP - */ +#include +#include + +#include #include "lwip/opt.h" #include "arch/sys_arch.h" #include "lwip/sys.h" #include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/debug.h" -#define tick_to_msec(tick) ((u16_t)((tick)*10+1)) -#define msec_to_tick(msec) ((cyg_tick_count_t)(msec+9)/10) +// Milliseconds per system tick +#define MS_PER_TICK ((u32_t) (CYGNUM_HAL_RTC_NUMERATOR / \ + (CYGNUM_HAL_RTC_DENOMINATOR * 1000000LL))) -/* We use a common var mempool for allocating semaphores, mboxes and threads... */ -static char memvar[CYGNUM_LWIP_VARMEMPOOL_SIZE]; -static cyg_mempool_var var_mempool; -static cyg_handle_t var_mempool_h; - +// Macros to convert between ticks and milliseconds +#define TICKS_TO_MS(_ticks_) ((u16_t) ((_ticks_) * MS_PER_TICK + 1)) +#define MS_TO_TICKS(_ms_) ((cyg_tick_count_t) (((_ms_) + \ + (MS_PER_TICK - 1)) / MS_PER_TICK)) -#define SYS_THREADS 2 /* polling thread and tcpip_thread */ +#if !NO_SYS -#define THREAD_COUNT (CYGNUM_LWIP_APP_THREADS + SYS_THREADS) -static char memfix[CYGNUM_LWIP_THREAD_STACK_SIZE * THREAD_COUNT]; - -/* List of threads: associate eCos thread info with lwIP timeout info */ +// Thread structure struct lwip_thread { - struct lwip_thread * next; - struct sys_timeouts to; - cyg_handle_t th; - cyg_thread t; -} *threads; + struct lwip_thread *next; // Next thread in linked list + struct sys_timeouts to; // List of timeouts + cyg_handle_t handle; // Thread handle + cyg_thread thread; // Thread store +}; + +// A var memory pool is used for allocating semaphores, mboxes and threads +static char var_data[CYGNUM_LWIP_VARMEMPOOL_SIZE]; +static cyg_mempool_var var_mempool; +static cyg_handle_t var_handle; + +// Internal lwip thread stacks are statically allocated +#define TOTAL_STACKSIZE (TCPIP_THREAD_STACKSIZE + \ + SLIPIF_THREAD_STACKSIZE + \ + PPP_THREAD_STACKSIZE + \ + ETH_THREAD_STACKSIZE) -/* - * Timeout for threads which were not created by sys_thread_new - * usually "main" - */ -struct sys_timeouts to; +static cyg_mutex_t stack_mutex; +static char stack_data[TOTAL_STACKSIZE]; +static char *stack_pos = stack_data; + +// Timeout for threads which were not created by sys_thread_new() +static struct sys_timeouts to; + +// List of threads +static struct lwip_thread *threads; -/* - * Set up memory pools and threads - */ -void sys_init(void) +// +// Is called to initialize the sys_arch layer. +// +void +sys_init(void) { - cyg_mempool_var_create(memvar, sizeof(memvar), &var_mempool_h, &var_mempool); - + cyg_mempool_var_create( + var_data, + sizeof(var_data), + &var_handle, + &var_mempool + ); + threads = NULL; to.next = NULL; + + cyg_mutex_init(&stack_mutex); } -/* - * Create a new mbox.If no memory is available return NULL - */ -sys_mbox_t sys_mbox_new(void) +// +// Creates and returns a new semaphore. The "count" argument specifies the +// initial state of the semaphore. +// +sys_sem_t +sys_sem_new(u8_t count) { - cyg_mbox * mbox; - cyg_handle_t m; - mbox = (cyg_mbox *)cyg_mempool_var_try_alloc(var_mempool_h, sizeof(cyg_mbox)); - - /* out of memory? */ - if(!mbox) - return SYS_MBOX_NULL; - - cyg_mbox_create(&m, mbox); - return m; + sys_sem_t sem; + + // Allocate semaphore + sem = (cyg_sem_t *) cyg_mempool_var_try_alloc(var_handle, sizeof(cyg_sem_t)); + if (!sem) + return SYS_SEM_NULL; + cyg_semaphore_init(sem, count); + +#if SYS_STATS + lwip_stats.sys.sem.used++; + if (lwip_stats.sys.sem.used > lwip_stats.sys.sem.max) + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; +#endif + + return sem; } -/* - * Destroy the mbox and release the space it took up in the pool - */ -void sys_mbox_free(sys_mbox_t mbox) +// +// Deallocates a semaphore. +// +void +sys_sem_free(sys_sem_t sem) { - cyg_mbox_delete(mbox); - cyg_mempool_var_free(var_mempool_h,(void*)mbox); + if (!sem) + return; + + cyg_semaphore_destroy(sem); + cyg_mempool_var_free(var_handle, (void *) sem); + +#if SYS_STATS + lwip_stats.sys.sem.used--; +#endif +} + +// +// Signals a semaphore. +// +void +sys_sem_signal(sys_sem_t sem) +{ + cyg_semaphore_post(sem); } -/* - * cyg_mbox_put should not be passed a NULL otherwise the cyg_mbox_get will not - * know if it's real data or error condition. But lwIP does pass NULL on occasion - * in cases when maybe using a semaphore would be better. So this dummy_msg replaces - * NULL data - */ - -int dummy_msg = 1; +// +// Blocks the thread while waiting for the semaphore to be signaled. If the +// "timeout" argument is non-zero, the thread should only be blocked for the +// specified time (measured in milliseconds). If the "timeout" argument is +// zero, the thread should be blocked until the semaphore is signalled. +// +// If the timeout argument is non-zero, the return value is the number of +// milliseconds spent waiting for the semaphore to be signaled. If the +// semaphore wasn't signaled within the specified time, the return value is +// SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore +// (i.e., it was already signaled), the function may return zero. +// +// Notice that lwIP implements a function with a similar name, sys_sem_wait(), +// that uses the sys_arch_sem_wait() function. +// +u32_t +sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) +{ + if (timeout) { + cyg_tick_count_t start_time = cyg_current_time(); -/* - * Post data to a mbox. - */ -void sys_mbox_post(sys_mbox_t mbox, void *data) -{ - if (!data) - data = &dummy_msg; - while (cyg_mbox_put(mbox,data) == false); + // Wait for semaphore with timeout + if (!cyg_semaphore_timed_wait(sem, start_time + MS_TO_TICKS(timeout))) + return SYS_ARCH_TIMEOUT; + // Return elapsed time + return TICKS_TO_MS(cyg_current_time() - start_time); + } else { + // Wait for semaphore indefinitely + cyg_semaphore_wait(sem); + return 0; + } } -#if 0 -void -sys_mbox_fetch(sys_mbox_t mbox, void **msg){ - void *d; - d = cyg_mbox_get(mbox); - if (msg) - *msg = d; - -} +// +// Creates an empty mailbox for maximum "size" elements. Elements stored in +// mailboxes are pointers. You have to define macros "_MBOX_SIZE" in your +// lwipopts.h, or ignore this parameter in your implementation and use a +// default size. +// +sys_mbox_t +sys_mbox_new(int size) +{ + cyg_mbox *mbox; + cyg_handle_t handle; + + LWIP_UNUSED_ARG(size); + + mbox = (cyg_mbox *) cyg_mempool_var_try_alloc(var_handle, sizeof(cyg_mbox)); + if (!mbox) + return SYS_MBOX_NULL; + cyg_mbox_create(&handle, mbox); + +#if SYS_STATS + lwip_stats.sys.mbox.used++; + if (lwip_stats.sys.mbox.used > lwip_stats.sys.mbox.max) + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; #endif -/* - * Fetch data from a mbox.Wait for at most timeout millisecs - * Return -1 if timed out otherwise time spent waiting. - */ -u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **data, u32_t timeout) + return handle; +} + +// +// Deallocates a mailbox. If there are messages still present in the mailbox +// when the mailbox is deallocated, it is an indication of a programming error +// in lwIP and the developer should be notified. +// +void +sys_mbox_free(sys_mbox_t mbox) { - void *d; - cyg_tick_count_t end_time = 0, start_time = 0; - if (timeout) { - start_time = cyg_current_time(); - d = cyg_mbox_timed_get(mbox, start_time + msec_to_tick(timeout)); - end_time = cyg_current_time(); + if (!mbox) + return; + + if (cyg_mbox_peek(mbox)) + LWIP_DEBUGF(SYS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("sys_mbox_free: mbox not empty\n")); + + cyg_mbox_delete(mbox); + cyg_mempool_var_free(var_handle, (void *) mbox); - if (d == NULL) - return SYS_ARCH_TIMEOUT; - } else { - d = cyg_mbox_get(mbox); - } +#if SYS_STATS + lwip_stats.sys.mbox.used--; +#endif +} - if (data) { - if (d == (void *)&dummy_msg) - *data = NULL; - else - *data = d; - } +// +// cyg_mbox_put() should not be passed a NULL, otherwise the cyg_mbox_get() +// will not know if it's real data or an error condition. But lwIP does pass +// NULL on occasion, in cases when maybe using a semaphore would be better. So +// this null_msg replaces NULL data. +// +static int null_msg; - return tick_to_msec(end_time - start_time); +// +// Posts the "msg" to the mailbox. This function have to block until the "msg" +// is really posted. +// +void +sys_mbox_post(sys_mbox_t mbox, void *msg) +{ + // Map NULL messages + if (!msg) + msg = &null_msg; + while (cyg_mbox_put(mbox, msg) == false); } -/* - * Create a new semaphore and initialize it. - * If no memory is available return NULL - */ -sys_sem_t sys_sem_new(u8_t count) +// +// Try to post the "msg" to the mailbox. Returns ERR_MEM if this one is full, +// else, ERR_OK if the "msg" is posted. +// +err_t +sys_mbox_trypost(sys_mbox_t mbox, void *msg) { - sys_sem_t sem; - - sem = (cyg_sem_t *)cyg_mempool_var_try_alloc(var_mempool_h, sizeof(cyg_sem_t)); - /* out of memory? */ - if(!sem) - return SYS_SEM_NULL; - cyg_semaphore_init(sem, count); - return sem; + // Map NULL messages + if (!msg) + msg = &null_msg; + return cyg_mbox_tryput(mbox, msg) ? ERR_OK : ERR_MEM; } -#if 0 -void -sys_sem_wait(sys_sem_t sem) +// +// Blocks the thread until a message arrives in the mailbox, but does not block +// the thread longer than "timeout" milliseconds (similar to the +// sys_arch_sem_wait() function). If "timeout" is 0, the thread should be +// blocked until a message arrives. The "msg" argument is a result parameter +// that is set by the function (i.e., by doing "*msg = ptr"). The "msg" +// parameter maybe NULL to indicate that the message should be dropped. +// +// The return values are the same as for the sys_arch_sem_wait() function: +// Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a +// timeout. +// +// Note that a function with a similar name, sys_mbox_fetch(), is implemented +// by lwIP. +// +u32_t +sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) { - cyg_semaphore_wait(sem); + void *m; + + if (timeout) { + cyg_tick_count_t start_time = cyg_current_time(); + // Wait for mailbox with timeout + if (!(m = cyg_mbox_timed_get(mbox, start_time + MS_TO_TICKS(timeout)))) + return SYS_ARCH_TIMEOUT; + // Map NULL messages + if (m == &null_msg) + m = NULL; + *msg = m; + // Return elapsed time + return TICKS_TO_MS(cyg_current_time() - start_time); + } else { + // Wait for semaphore indefinitely + m = cyg_mbox_get(mbox); + // Map NULL messages + if (m == &null_msg) + m = NULL; + *msg = m; + return 0; + } } -void -sys_timeout(u16_t msecs, sys_timeout_handler h, void *arg) -{} -#endif -/* - * Wait on a semaphore for at most timeout millisecs - * Return -1 if timed out otherwise time spent waiting. - */ -u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) +// +// This is similar to sys_arch_mbox_fetch, however if a message is not present +// in the mailbox, it immediately returns with the code SYS_MBOX_EMPTY. On +// success 0 is returned. +// +// To allow for efficient implementations, this can be defined as a +// function-like macro in sys_arch.h instead of a normal function. For example, +// a naive implementation could be: +// +// #define sys_arch_mbox_tryfetch(mbox,msg) sys_arch_mbox_fetch(mbox,msg,1) +// +// although this would introduce unnecessary delays. +// +u32_t +sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg) { - cyg_bool_t r; - cyg_tick_count_t end_time = 0, start_time = 0; + void *m; - if (timeout) { - start_time = cyg_current_time(); - r = cyg_semaphore_timed_wait(sem, start_time + msec_to_tick(timeout)); - end_time = cyg_current_time(); + m = cyg_mbox_tryget(mbox); + if (!m) + return SYS_MBOX_EMPTY; - if (r == false) { - return SYS_ARCH_TIMEOUT; - } - } else { - cyg_semaphore_wait(sem); - } + if (m == &null_msg) + m = NULL; + *msg = m; - return tick_to_msec(end_time - start_time); + return 0; } -/* - * Signal a semaphore - */ -void sys_sem_signal(sys_sem_t sem) +// +// Returns a pointer to the per-thread sys_timeouts structure. In lwIP, each +// thread has a list of timeouts which is repressented as a linked list of +// sys_timeout structures. The sys_timeouts structure holds a pointer to a +// linked list of timeouts. This function is called by the lwIP timeout +// scheduler and must not return a NULL value. +// +// In a single thread sys_arch implementation, this function will simply return +// a pointer to a global sys_timeouts variable stored in the sys_arch module. +// +struct sys_timeouts * +sys_arch_timeouts(void) { - cyg_semaphore_post(sem); + cyg_handle_t handle; + struct lwip_thread *t; + + handle = cyg_thread_self(); + for (t = threads; t; t = t->next) + if (t->handle == handle) + return &(t->to); + + return &to; +} + +// +// Starts a new thread named "name" with priority "prio" that will begin its +// execution in the function "thread()". The "arg" argument will be passed as +// an argument to the thread() function. The stack size to used for this thread +// is the "stacksize" parameter. The id of the new thread is returned. Both the +// id and the priority are system dependent. +// +sys_thread_t +sys_thread_new(char *name, void (* thread)(void *arg), void *arg, + int stacksize, int prio) +{ + void *stack; + + cyg_mutex_lock(&stack_mutex); + stack = stack_pos; + stack_pos += stacksize; + cyg_mutex_unlock(&stack_mutex); + + if (stack_pos > stack_data + TOTAL_STACKSIZE) + CYG_FAIL("Not enough memory to allocate the thread's stack. You may " + "want to use cyg_lwip_thread_new() instead of " + "sys_thread_new() so you can provide external stack memory."); + + return cyg_lwip_thread_new(name, thread, arg, stack, stacksize, prio); } -/* - * Destroy the semaphore and release the space it took up in the pool - */ -void sys_sem_free(sys_sem_t sem) +// +// Basically implements the sys_thread_new() call, but adds a "stack" parameter, +// allowing clients to provide their own stack buffers. +// +sys_thread_t +cyg_lwip_thread_new(char *name, void (* thread)(void *arg), void *arg, + void *stack, int stacksize, int prio) { - cyg_semaphore_destroy(sem); - cyg_mempool_var_free(var_mempool_h,(void*)sem); + struct lwip_thread *t; + + t = (struct lwip_thread *) cyg_mempool_var_alloc( + var_handle, sizeof(struct lwip_thread)); + + t->next = threads; + t->to.next = NULL; + + threads = t; + + cyg_thread_create( + prio, + (cyg_thread_entry_t *) thread, + (cyg_addrword_t) arg, + name, + stack, + stacksize, + &t->handle, + &t->thread + ); + cyg_thread_resume(t->handle); + + return t->handle; } -/* - * Create new thread - */ -sys_thread_t sys_thread_new(void (*function) (void *arg), void *arg,int prio) -{ - struct lwip_thread * nt; - void * stack; - static int thread_count = 0; - nt = (struct lwip_thread *)cyg_mempool_var_alloc(var_mempool_h, sizeof(struct lwip_thread)); +#endif // !NO_SYS - nt->next = threads; - nt->to.next = NULL; - - threads = nt; - - stack = (void *)(memfix+CYGNUM_LWIP_THREAD_STACK_SIZE*thread_count++); - cyg_thread_create(prio, (cyg_thread_entry_t *)function, (cyg_addrword_t)arg, - (char *)arg , stack, CYGNUM_LWIP_THREAD_STACK_SIZE, &(nt->th), &(nt->t) ); - - cyg_thread_resume(nt->th); - return nt->th; +// +// Returns the current time in milliseconds. +// +u32_t +sys_now(void) +{ + return cyg_current_time() * MS_PER_TICK; } -/* - * Return current thread's timeout info - */ -struct sys_timeouts *sys_arch_timeouts(void) +// +// This optional function does a "fast" critical region protection and returns +// the previous protection level. This function is only called during very short +// critical regions. An embedded system which supports ISR-based drivers might +// want to implement this function by disabling interrupts. Task-based systems +// might want to implement this by using a mutex or disabling tasking. This +// function should support recursive calls from the same task or interrupt. In +// other words, sys_arch_protect() could be called while already protected. In +// that case the return value indicates that it is already protected. +// +// sys_arch_protect() is only required if your port is supporting an operating +// system. +// +sys_prot_t +sys_arch_protect(void) { - cyg_handle_t ct; - struct lwip_thread *t; + cyg_scheduler_lock(); + + return 0; +} - ct = cyg_thread_self(); - for(t = threads; t; t = t->next) - if (t->th == ct) - return &(t->to); - - return &to; +// +// This optional function does a "fast" set of critical region protection to the +// value specified by pval. See the documentation for sys_arch_protect() for +// more information. This function is only required if your port is supporting +// an operating system. +// +void +sys_arch_unprotect(sys_prot_t pval) +{ + LWIP_UNUSED_ARG(pval); + + cyg_scheduler_unlock(); } diff --git a/packages/net/lwip_tcpip/current/src/netif/etharp.c b/packages/net/lwip_tcpip/current/src/netif/etharp.c --- a/packages/net/lwip_tcpip/current/src/netif/etharp.c +++ b/packages/net/lwip_tcpip/current/src/netif/etharp.c @@ -8,8 +8,7 @@ * * This implementation complies with RFC 826 (Ethernet ARP). It supports * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 - * if an interface calls etharp_query(our_netif, its_ip_addr, NULL) upon - * address change. + * if an interface calls etharp_gratuitous(our_netif) upon address change. */ /* @@ -43,36 +42,41 @@ * This file is part of the lwIP TCP/IP stack. * */ - + #include "lwip/opt.h" + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + #include "lwip/inet.h" -#include "netif/etharp.h" #include "lwip/ip.h" #include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" -/* ARP needs to inform DHCP of any ARP replies? */ -#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) -# include "lwip/dhcp.h" -#endif +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include /** the time an ARP entry stays valid after its last update, - * (240 * 5) seconds = 20 minutes. + * for ARP_TMR_INTERVAL = 5000, this is + * (240 * 5) seconds = 20 minutes. */ #define ARP_MAXAGE 240 /** the time an ARP entry stays pending after first request, - * (2 * 5) seconds = 10 seconds. + * for ARP_TMR_INTERVAL = 5000, this is + * (2 * 5) seconds = 10 seconds. * - * @internal Keep this number at least 2, otherwise it might - * run out instantly if the timeout occurs directly after a request. + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. */ #define ARP_MAXPENDING 2 #define HWTYPE_ETHERNET 1 -/** ARP message types */ -#define ARP_REQUEST 1 -#define ARP_REPLY 2 - #define ARPH_HWLEN(hdr) (ntohs((hdr)->_hwlen_protolen) >> 8) #define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff) @@ -80,11 +84,9 @@ #define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8)) enum etharp_state { - ETHARP_STATE_EMPTY, + ETHARP_STATE_EMPTY = 0, ETHARP_STATE_PENDING, - ETHARP_STATE_STABLE, - /** @internal transitional state used in etharp_tmr() for convenience*/ - ETHARP_STATE_EXPIRED + ETHARP_STATE_STABLE }; struct etharp_entry { @@ -92,40 +94,66 @@ struct etharp_entry { /** * Pointer to queue of pending outgoing packets on this ARP entry. */ - struct pbuf *p; + struct etharp_q_entry *q; #endif struct ip_addr ipaddr; struct eth_addr ethaddr; enum etharp_state state; u8_t ctime; + struct netif *netif; }; -static const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; static struct etharp_entry arp_table[ARP_TABLE_SIZE]; +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif /** * Try hard to create a new entry - we want the IP address to appear in * the cache (even if this means removing an active entry or so). */ #define ETHARP_TRY_HARD 1 +#define ETHARP_FIND_ONLY 2 +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif); +#else /* LWIP_NETIF_HWADDRHINT */ static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags); +#endif /* LWIP_NETIF_HWADDRHINT */ + static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags); + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, you have to reduce it in your lwipopts.h" +#endif + + +#if ARP_QUEUEING /** - * Initializes ARP module. + * Free a complete queue of etharp entries + * + * @param q a qeueue of etharp_q_entry's to free */ -void -etharp_init(void) +static void +free_etharp_q(struct etharp_q_entry *q) { - u8_t i; - /* clear ARP entries */ - for(i = 0; i < ARP_TABLE_SIZE; ++i) { - arp_table[i].state = ETHARP_STATE_EMPTY; -#if ARP_QUEUEING - arp_table[i].p = NULL; -#endif - arp_table[i].ctime = 0; + struct etharp_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ARP_QUEUE, r); } } +#endif /** * Clears expired entries in the ARP table. @@ -142,38 +170,34 @@ etharp_tmr(void) /* remove expired entries from the ARP table */ for (i = 0; i < ARP_TABLE_SIZE; ++i) { arp_table[i].ctime++; - /* stable entry? */ - if ((arp_table[i].state == ETHARP_STATE_STABLE) && - /* entry has become old? */ - (arp_table[i].ctime >= ARP_MAXAGE)) { - LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired stable entry %"U16_F".\n", (u16_t)i)); - arp_table[i].state = ETHARP_STATE_EXPIRED; - /* pending entry? */ - } else if (arp_table[i].state == ETHARP_STATE_PENDING) { - /* entry unresolved/pending for too long? */ - if (arp_table[i].ctime >= ARP_MAXPENDING) { - LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired pending entry %"U16_F".\n", (u16_t)i)); - arp_table[i].state = ETHARP_STATE_EXPIRED; -#if ARP_QUEUEING - } else if (arp_table[i].p != NULL) { - /* resend an ARP query here */ -#endif - } - } - /* clean up entries that have just been expired */ - if (arp_table[i].state == ETHARP_STATE_EXPIRED) { + if (((arp_table[i].state == ETHARP_STATE_STABLE) && + (arp_table[i].ctime >= ARP_MAXAGE)) || + ((arp_table[i].state == ETHARP_STATE_PENDING) && + (arp_table[i].ctime >= ARP_MAXPENDING))) { + /* pending or stable entry has become old! */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", + arp_table[i].state == ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + /* clean up entries that have just been expired */ + /* remove from SNMP ARP index tree */ + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); #if ARP_QUEUEING /* and empty packet queue */ - if (arp_table[i].p != NULL) { + if (arp_table[i].q != NULL) { /* remove all queued packets */ - LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].p))); - pbuf_free(arp_table[i].p); - arp_table[i].p = NULL; + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; } #endif /* recycle entry for re-use */ arp_table[i].state = ETHARP_STATE_EMPTY; } +#if ARP_QUEUEING + /* still pending entry? (not expired) */ + if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + } +#endif } } @@ -199,7 +223,12 @@ etharp_tmr(void) * @return The ARP entry index that matched or is created, ERR_MEM if no * entry is found or could be recycled. */ -static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags) +static s8_t +#if LWIP_NETIF_HWADDRHINT +find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif) +#else /* LWIP_NETIF_HWADDRHINT */ +find_entry(struct ip_addr *ipaddr, u8_t flags) +#endif /* LWIP_NETIF_HWADDRHINT */ { s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; s8_t empty = ARP_TABLE_SIZE; @@ -211,6 +240,35 @@ static s8_t find_entry(struct ip_addr *i u8_t age_queue = 0; #endif + /* First, test if the last call to this function asked for the + * same address. If so, we're really fast! */ + if (ipaddr) { + /* ipaddr to search for was given */ +#if LWIP_NETIF_HWADDRHINT + if ((netif != NULL) && (netif->addr_hint != NULL)) { + /* per-pcb cached entry was given */ + u8_t per_pcb_cache = *(netif->addr_hint); + if ((per_pcb_cache < ARP_TABLE_SIZE) && arp_table[per_pcb_cache].state == ETHARP_STATE_STABLE) { + /* the per-pcb-cached entry is stable */ + if (ip_addr_cmp(ipaddr, &arp_table[per_pcb_cache].ipaddr)) { + /* per-pcb cached entry was the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return per_pcb_cache; + } + } + } +#else /* #if LWIP_NETIF_HWADDRHINT */ + if (arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) { + /* the cached entry is stable */ + if (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr)) { + /* cached entry was the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_cached_entry; + } + } +#endif /* #if LWIP_NETIF_HWADDRHINT */ + } + /** * a) do a search through the cache, remember candidates * b) select candidate entry @@ -237,12 +295,17 @@ static s8_t find_entry(struct ip_addr *i else if (arp_table[i].state == ETHARP_STATE_PENDING) { /* if given, does IP address match IP address in ARP entry? */ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i)); /* found exact IP address match, simply bail out */ +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ return i; #if ARP_QUEUEING /* pending with queued packets? */ - } else if (arp_table[i].p != NULL) { + } else if (arp_table[i].q != NULL) { if (arp_table[i].ctime >= age_queue) { old_queue = i; age_queue = arp_table[i].ctime; @@ -260,8 +323,13 @@ static s8_t find_entry(struct ip_addr *i else if (arp_table[i].state == ETHARP_STATE_STABLE) { /* if given, does IP address match IP address in ARP entry? */ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i)); /* found exact IP address match, simply bail out */ +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ return i; /* remember entry with oldest stable entry in oldest, its age in maxtime */ } else if (arp_table[i].ctime >= age_stable) { @@ -273,16 +341,18 @@ static s8_t find_entry(struct ip_addr *i /* { we have no match } => try to create a new entry */ /* no empty entry found and not allowed to recycle? */ - if ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0)) - { - return (s8_t)ERR_MEM; + if (((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0)) + /* or don't create new entry, only search? */ + || ((flags & ETHARP_FIND_ONLY) != 0)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n")); + return (s8_t)ERR_MEM; } /* b) choose the least destructive entry to recycle: * 1) empty entry * 2) oldest stable entry * 3) oldest pending entry without queued packets - * 4) oldest pending entry without queued packets + * 4) oldest pending entry with queued packets * * { ETHARP_TRY_HARD is set at this point } */ @@ -290,30 +360,30 @@ static s8_t find_entry(struct ip_addr *i /* 1) empty entry available? */ if (empty < ARP_TABLE_SIZE) { i = empty; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); } /* 2) found recyclable stable entry? */ else if (old_stable < ARP_TABLE_SIZE) { /* recycle oldest stable*/ i = old_stable; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); #if ARP_QUEUEING /* no queued packets should exist on stable entries */ - LWIP_ASSERT("arp_table[i].p == NULL", arp_table[i].p == NULL); + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); #endif /* 3) found recyclable pending entry without queued packets? */ } else if (old_pending < ARP_TABLE_SIZE) { /* recycle oldest pending */ i = old_pending; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); #if ARP_QUEUEING /* 4) found recyclable pending entry with queued packets? */ } else if (old_queue < ARP_TABLE_SIZE) { /* recycle oldest pending */ i = old_queue; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].p))); - pbuf_free(arp_table[i].p); - arp_table[i].p = NULL; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; #endif /* no empty or recyclable entries found */ } else { @@ -323,6 +393,10 @@ static s8_t find_entry(struct ip_addr *i /* { empty or recyclable entry found } */ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + if (arp_table[i].state != ETHARP_STATE_EMPTY) + { + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); + } /* recycle entry (no-op for an already empty entry) */ arp_table[i].state = ETHARP_STATE_EMPTY; @@ -332,10 +406,45 @@ static s8_t find_entry(struct ip_addr *i ip_addr_set(&arp_table[i].ipaddr, ipaddr); } arp_table[i].ctime = 0; +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ return (err_t)i; } /** + * Send an IP packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = p->payload; + u8_t k; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + k = ETHARP_HWADDR_LEN; + while(k > 0) { + k--; + ethhdr->dest.addr[k] = dst->addr[k]; + ethhdr->src.addr[k] = src->addr[k]; + } + ethhdr->type = htons(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** * Update (or insert) a IP/MAC address pair in the ARP cache. * * If a pending entry is resolved, any queued packets will be sent @@ -357,10 +466,11 @@ static s8_t find_entry(struct ip_addr *i static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags) { - s8_t i, k; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 3, ("update_arp_entry()\n")); - LWIP_ASSERT("netif->hwaddr_len != 0", netif->hwaddr_len != 0); - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + s8_t i; + u8_t k; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry()\n")); + LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); @@ -368,43 +478,50 @@ update_arp_entry(struct netif *netif, st if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); return ERR_ARG; } /* find or create ARP entry */ +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, flags, netif); +#else /* LWIP_NETIF_HWADDRHINT */ i = find_entry(ipaddr, flags); +#endif /* LWIP_NETIF_HWADDRHINT */ /* bail out if no entry could be found */ - if (i < 0) return (err_t)i; + if (i < 0) + return (err_t)i; /* mark it stable */ arp_table[i].state = ETHARP_STATE_STABLE; + /* record network interface */ + arp_table[i].netif = netif; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); /* update address */ - for (k = 0; k < netif->hwaddr_len; ++k) { + k = ETHARP_HWADDR_LEN; + while (k > 0) { + k--; arp_table[i].ethaddr.addr[k] = ethaddr->addr[k]; } /* reset time stamp */ arp_table[i].ctime = 0; -/* this is where we will send out queued packets! */ #if ARP_QUEUEING - while (arp_table[i].p != NULL) { - /* get the first packet on the queue */ - struct pbuf *p = arp_table[i].p; - /* Ethernet header */ - struct eth_hdr *ethhdr = p->payload; - /* remember (and reference) remainder of queue */ - /* note: this will also terminate the p pbuf chain */ - arp_table[i].p = pbuf_dequeue(p); - /* fill-in Ethernet header */ - for (k = 0; k < netif->hwaddr_len; ++k) { - ethhdr->dest.addr[k] = ethaddr->addr[k]; - ethhdr->src.addr[k] = netif->hwaddr[k]; - } - ethhdr->type = htons(ETHTYPE_IP); - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: sending queued IP packet %p.\n", (void *)p)); + /* this is where we will send out queued packets! */ + while (arp_table[i].q != NULL) { + struct pbuf *p; + /* remember remainder of queue */ + struct etharp_q_entry *q = arp_table[i].q; + /* pop first item off the queue */ + arp_table[i].q = q->next; + /* get the packet pointer */ + p = q->p; + /* now queue entry can be freed */ + memp_free(MEMP_ARP_QUEUE, q); /* send the queued IP packet */ - netif->linkoutput(netif, p); + etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); /* free the queued IP packet */ pbuf_free(p); } @@ -413,6 +530,38 @@ update_arp_entry(struct netif *netif, st } /** + * Finds (stable) ethernet/IP address pair from ARP table + * using interface and IP address index. + * @note the addresses in the ARP table are in network order! + * + * @param netif points to interface index + * @param ipaddr points to the (network order) IP address index + * @param eth_ret points to return pointer + * @param ip_ret points to return pointer + * @return table index if found, -1 otherwise + */ +s8_t +etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr, + struct eth_addr **eth_ret, struct ip_addr **ip_ret) +{ + s8_t i; + + LWIP_UNUSED_ARG(netif); + +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, ETHARP_FIND_ONLY, NULL); +#else /* LWIP_NETIF_HWADDRHINT */ + i = find_entry(ipaddr, ETHARP_FIND_ONLY); +#endif /* LWIP_NETIF_HWADDRHINT */ + if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) { + *eth_ret = &arp_table[i].ethaddr; + *ip_ret = &arp_table[i].ipaddr; + return i; + } + return -1; +} + +/** * Updates the ARP table using the given IP packet. * * Uses the incoming IP packet's source address to update the @@ -421,7 +570,7 @@ update_arp_entry(struct netif *netif, st * packet p is passed to the IP layer. * * @param netif The lwIP network interface on which the IP packet pbuf arrived. - * @param pbuf The IP packet that arrived on netif. + * @param p The IP packet that arrived on netif. * * @return NULL * @@ -430,22 +579,30 @@ update_arp_entry(struct netif *netif, st void etharp_ip_input(struct netif *netif, struct pbuf *p) { - struct ethip_hdr *hdr; - LWIP_ASSERT("netif != NULL", netif != NULL); + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + LWIP_ERROR("netif != NULL", (netif != NULL), return;); /* Only insert an entry if the source IP address of the incoming IP packet comes from a host on the local network. */ - hdr = p->payload; + ethhdr = p->payload; + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == ETHTYPE_VLAN) { + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + /* source is not on the local network? */ - if (!ip_addr_netcmp(&(hdr->ip.src), &(netif->ip_addr), &(netif->netmask))) { + if (!ip_addr_netcmp(&(iphdr->src), &(netif->ip_addr), &(netif->netmask))) { /* do nothing */ return; } - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); /* update ARP table */ /* @todo We could use ETHARP_TRY_HARD if we think we are going to talk * back soon (for example, if the destination IP address is ours. */ - update_arp_entry(netif, &(hdr->ip.src), &(hdr->eth.src), 0); + update_arp_entry(netif, &(iphdr->src), &(ethhdr->src), 0); } @@ -457,8 +614,8 @@ etharp_ip_input(struct netif *netif, str * is freed by this function. * * @param netif The lwIP network interface on which the ARP packet pbuf arrived. - * @param pbuf The ARP packet that arrived on netif. Is freed by this function. * @param ethaddr Ethernet address of netif. + * @param p The ARP packet that arrived on netif. Is freed by this function. * * @return NULL * @@ -468,25 +625,63 @@ void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) { struct etharp_hdr *hdr; + struct eth_hdr *ethhdr; /* these are aligned properly, whereas the ARP header fields might not be */ struct ip_addr sipaddr, dipaddr; u8_t i; u8_t for_us; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ - LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ERROR("netif != NULL", (netif != NULL), return;); - /* drop short ARP packets */ - if (p->tot_len < sizeof(struct etharp_hdr)) { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, sizeof(struct etharp_hdr))); + /* drop short ARP packets: we have to check for p->len instead of p->tot_len here + since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */ + if (p->len < SIZEOF_ETHARP_PACKET) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, + (s16_t)SIZEOF_ETHARP_PACKET)); + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); pbuf_free(p); return; } - hdr = p->payload; - - /* get aligned copies of addresses */ - *(struct ip_addr2 *)&sipaddr = hdr->sipaddr; - *(struct ip_addr2 *)&dipaddr = hdr->dipaddr; + ethhdr = p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == ETHTYPE_VLAN) { + hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + /* RFC 826 "Packet Reception": */ + if ((hdr->hwtype != htons(HWTYPE_ETHERNET)) || + (hdr->_hwlen_protolen != htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr))) || + (hdr->proto != htons(ETHTYPE_IP)) || + (ethhdr->type != htons(ETHTYPE_ARP))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, ARPH_HWLEN(hdr), hdr->proto, ARPH_PROTOLEN(hdr), ethhdr->type)); + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + ETHARP_STATS_INC(etharp.recv); + +#if LWIP_AUTOIP + /* We have to check if a host already has configured our random + * created link local address and continously check if there is + * a host with this IP-address so we can detect collisions */ + autoip_arp_reply(netif, hdr); +#endif /* LWIP_AUTOIP */ + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). */ + SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); + SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr)); /* this interface is not configured? */ if (netif->ip_addr.addr == 0) { @@ -515,57 +710,69 @@ etharp_arp_input(struct netif *netif, st * reply. In any case, we time-stamp any existing ARP entry, * and possiby send out an IP packet that was queued on it. */ - LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); + LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); /* ARP request for our address? */ if (for_us) { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); - /* re-use pbuf to send ARP reply */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); + /* Re-use pbuf to send ARP reply. + Since we are re-using an existing pbuf, we can't call etharp_raw since + that would allocate a new pbuf. */ hdr->opcode = htons(ARP_REPLY); hdr->dipaddr = hdr->sipaddr; - hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; + SMEMCPY(&hdr->sipaddr, &netif->ip_addr, sizeof(hdr->sipaddr)); - for(i = 0; i < netif->hwaddr_len; ++i) { + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + i = ETHARP_HWADDR_LEN; +#if LWIP_AUTOIP + /* If we are using Link-Local, ARP packets must be broadcast on the + * link layer. (See RFC3927 Section 2.5) */ + ethdst_hwaddr = ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr; +#endif /* LWIP_AUTOIP */ + + while(i > 0) { + i--; hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; +#if LWIP_AUTOIP + ethhdr->dest.addr[i] = ethdst_hwaddr[i]; +#else /* LWIP_AUTOIP */ + ethhdr->dest.addr[i] = hdr->shwaddr.addr[i]; +#endif /* LWIP_AUTOIP */ hdr->shwaddr.addr[i] = ethaddr->addr[i]; - hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; - hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; + ethhdr->src.addr[i] = ethaddr->addr[i]; } - hdr->hwtype = htons(HWTYPE_ETHERNET); - ARPH_HWLEN_SET(hdr, netif->hwaddr_len); + /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header + are already correct, we tested that before */ - hdr->proto = htons(ETHTYPE_IP); - ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); - - hdr->ethhdr.type = htons(ETHTYPE_ARP); /* return ARP reply */ netif->linkoutput(netif, p); /* we are not configured? */ } else if (netif->ip_addr.addr == 0) { /* { for_us == 0 and netif->ip_addr.addr == 0 } */ - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); /* request was not directed to us */ } else { /* { for_us == 0 and netif->ip_addr.addr != 0 } */ - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); } break; case ARP_REPLY: /* ARP reply. We already updated the ARP cache earlier. */ - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) /* DHCP wants to know about ARP replies from any host with an * IP address also offered to us by the DHCP server. We do not * want to take a duplicate IP address on a single network. - * @todo How should we handle redundant (fail-over) interfaces? - * */ + * @todo How should we handle redundant (fail-over) interfaces? */ dhcp_arp_reply(netif, &sipaddr); #endif break; default: - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + ETHARP_STATS_INC(etharp.err); break; } /* free ARP packet */ @@ -573,7 +780,7 @@ etharp_arp_input(struct netif *netif, st } /** - * Resolve and fill-in Ethernet address header for outgoing packet. + * Resolve and fill-in Ethernet address header for outgoing IP packet. * * For IP multicast and broadcast, corresponding Ethernet addresses * are selected and the packet is transmitted on the link. @@ -583,24 +790,23 @@ etharp_arp_input(struct netif *netif, st * the gateway is used. * * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. * @param ipaddr The IP address of the packet destination. - * @param pbuf The pbuf(s) containing the IP packet to be sent. * * @return * - ERR_RTE No route to destination (no gateway to external networks), - * or the return type of either etharp_query() or netif->linkoutput(). + * or the return type of either etharp_query() or etharp_send_ip(). */ err_t -etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) +etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr) { - struct eth_addr *dest, *srcaddr, mcastaddr; - struct eth_hdr *ethhdr; - u8_t i; + struct eth_addr *dest, mcastaddr; /* make room for Ethernet header - should not fail */ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { /* bail out */ - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_output: could not allocate room for header.\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); LINK_STATS_INC(link.lenerr); return ERR_BUF; } @@ -645,15 +851,8 @@ etharp_output(struct netif *netif, struc /* continuation for multicast/broadcast destinations */ /* obtain source Ethernet address of the given interface */ - srcaddr = (struct eth_addr *)netif->hwaddr; - ethhdr = q->payload; - for (i = 0; i < netif->hwaddr_len; i++) { - ethhdr->dest.addr[i] = dest->addr[i]; - ethhdr->src.addr[i] = srcaddr->addr[i]; - } - ethhdr->type = htons(ETHTYPE_IP); /* send packet directly on the link */ - return netif->linkoutput(netif, q); + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest); } /** @@ -678,6 +877,8 @@ etharp_output(struct netif *netif, struc * @param q If non-NULL, a pbuf that must be delivered to the IP address. * q is not freed by this function. * + * @note q must only be ONE packet, not a packet queue! + * * @return * - ERR_BUF Could not make room for Ethernet header. * - ERR_MEM Hardware address unknown, and no more ARP entries available @@ -687,29 +888,35 @@ etharp_output(struct netif *netif, struc * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. * */ -err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) +err_t +etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) { struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; err_t result = ERR_MEM; s8_t i; /* ARP entry index */ - u8_t k; /* Ethernet address octet index */ /* non-unicast address? */ if (ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr) || ip_addr_isany(ipaddr)) { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); return ERR_ARG; } /* find entry in ARP cache, ask to create entry if queueing packet */ +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, ETHARP_TRY_HARD, netif); +#else /* LWIP_NETIF_HWADDRHINT */ i = find_entry(ipaddr, ETHARP_TRY_HARD); +#endif /* LWIP_NETIF_HWADDRHINT */ /* could not find or create entry? */ - if (i < 0) - { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not create ARP entry\n")); - if (q) LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: packet dropped\n")); + if (i < 0) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n")); + ETHARP_STATS_INC(etharp.memerr); + } return (err_t)i; } @@ -727,105 +934,289 @@ err_t etharp_query(struct netif *netif, if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { /* try to resolve it; send out ARP request */ result = etharp_request(netif, ipaddr); + if (result != ERR_OK) { + /* ARP request couldn't be sent */ + /* We don't re-send arp request in etharp_tmr, but we still queue packets, + since this failure could be temporary, and the next packet calling + etharp_query again could lead to sending the queued packets. */ + } } /* packet given? */ if (q != NULL) { /* stable entry? */ if (arp_table[i].state == ETHARP_STATE_STABLE) { - /* we have a valid IP->Ethernet address mapping, - * fill in the Ethernet header for the outgoing packet */ - struct eth_hdr *ethhdr = q->payload; - for(k = 0; k < netif->hwaddr_len; k++) { - ethhdr->dest.addr[k] = arp_table[i].ethaddr.addr[k]; - ethhdr->src.addr[k] = srcaddr->addr[k]; - } - ethhdr->type = htons(ETHTYPE_IP); - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending packet %p\n", (void *)q)); + /* we have a valid IP->Ethernet address mapping */ /* send the packet */ - result = netif->linkoutput(netif, q); + result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); /* pending entry? (either just created or already pending */ } else if (arp_table[i].state == ETHARP_STATE_PENDING) { #if ARP_QUEUEING /* queue the given q packet */ struct pbuf *p; - /* copy any PBUF_REF referenced payloads into PBUF_RAM */ - /* (the caller of lwIP assumes the referenced payload can be - * freed after it returns from the lwIP call that brought us here) */ - p = pbuf_take(q); + int copy_needed = 0; + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0)); + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } /* packet could be taken over? */ if (p != NULL) { /* queue packet ... */ - if (arp_table[i].p == NULL) { - /* ... in the empty queue */ - pbuf_ref(p); - arp_table[i].p = p; -#if 0 /* multi-packet-queueing disabled, see bug #11400 */ + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_entry = memp_malloc(MEMP_ARP_QUEUE); + if (new_entry != NULL) { + new_entry->next = 0; + new_entry->p = p; + if(arp_table[i].q != NULL) { + /* queue was already existent, append the new entry to the end */ + struct etharp_q_entry *r; + r = arp_table[i].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; } else { - /* ... at tail of non-empty queue */ - pbuf_queue(arp_table[i].p, p); -#endif + /* the pool MEMP_ARP_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ } - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); - result = ERR_OK; } else { - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + ETHARP_STATS_INC(etharp.memerr); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); /* { result == ERR_MEM } through initialization */ } #else /* ARP_QUEUEING == 0 */ /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */ /* { result == ERR_MEM } through initialization */ - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q)); #endif } } return result; } -err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr) +/** + * Send a raw ARP packet (opcode and all addresses can be modified) + * + * @param netif the lwip network interface on which to send the ARP packet + * @param ethsrc_addr the source MAC address for the ethernet header + * @param ethdst_addr the destination MAC address for the ethernet header + * @param hwsrc_addr the source MAC address for the ARP protocol header + * @param ipsrc_addr the source IP address for the ARP protocol header + * @param hwdst_addr the destination MAC address for the ARP protocol header + * @param ipdst_addr the destination IP address for the ARP protocol header + * @param opcode the type of the ARP packet + * @return ERR_OK if the ARP packet has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +#if !LWIP_AUTOIP +static +#endif /* LWIP_AUTOIP */ +err_t +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const struct ip_addr *ipsrc_addr, + const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, + const u16_t opcode) { struct pbuf *p; - struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; err_t result = ERR_OK; u8_t k; /* ARP entry index */ + struct eth_hdr *ethhdr; + struct etharp_hdr *hdr; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ /* allocate a pbuf for the outgoing ARP request packet */ - p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM); + p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM); /* could allocate a pbuf for an ARP request? */ - if (p != NULL) { - struct etharp_hdr *hdr = p->payload; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_request: sending ARP request.\n")); - hdr->opcode = htons(ARP_REQUEST); - for (k = 0; k < netif->hwaddr_len; k++) - { - hdr->shwaddr.addr[k] = srcaddr->addr[k]; - /* the hardware address is what we ask for, in - * a request it is a don't-care value, we use zeroes */ - hdr->dhwaddr.addr[k] = 0x00; - } - hdr->dipaddr = *(struct ip_addr2 *)ipaddr; - hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; + if (p == NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_raw: could not allocate pbuf for ARP request.\n")); + ETHARP_STATS_INC(etharp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr", + (p->len >= SIZEOF_ETHARP_PACKET)); - hdr->hwtype = htons(HWTYPE_ETHERNET); - ARPH_HWLEN_SET(hdr, netif->hwaddr_len); + ethhdr = p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n")); + hdr->opcode = htons(opcode); - hdr->proto = htons(ETHTYPE_IP); - ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); - for (k = 0; k < netif->hwaddr_len; ++k) - { - /* broadcast to all network interfaces on the local network */ - hdr->ethhdr.dest.addr[k] = 0xff; - hdr->ethhdr.src.addr[k] = srcaddr->addr[k]; - } - hdr->ethhdr.type = htons(ETHTYPE_ARP); - /* send ARP query */ - result = netif->linkoutput(netif, p); - /* free ARP query packet */ - pbuf_free(p); - p = NULL; + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + k = ETHARP_HWADDR_LEN; +#if LWIP_AUTOIP + /* If we are using Link-Local, ARP packets must be broadcast on the + * link layer. (See RFC3927 Section 2.5) */ + ethdst_hwaddr = ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr; +#endif /* LWIP_AUTOIP */ + /* Write MAC-Addresses (combined loop for both headers) */ + while(k > 0) { + k--; + /* Write the ARP MAC-Addresses */ + hdr->shwaddr.addr[k] = hwsrc_addr->addr[k]; + hdr->dhwaddr.addr[k] = hwdst_addr->addr[k]; + /* Write the Ethernet MAC-Addresses */ +#if LWIP_AUTOIP + ethhdr->dest.addr[k] = ethdst_hwaddr[k]; +#else /* LWIP_AUTOIP */ + ethhdr->dest.addr[k] = ethdst_addr->addr[k]; +#endif /* LWIP_AUTOIP */ + ethhdr->src.addr[k] = ethsrc_addr->addr[k]; + } + hdr->sipaddr = *(struct ip_addr2 *)ipsrc_addr; + hdr->dipaddr = *(struct ip_addr2 *)ipdst_addr; + + hdr->hwtype = htons(HWTYPE_ETHERNET); + hdr->proto = htons(ETHTYPE_IP); + /* set hwlen and protolen together */ + hdr->_hwlen_protolen = htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr)); + + ethhdr->type = htons(ETHTYPE_ARP); + /* send ARP query */ + result = netif->linkoutput(netif, p); + ETHARP_STATS_INC(etharp.xmit); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; /* could not allocate pbuf for ARP request */ - } else { - result = ERR_MEM; - LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_request: could not allocate pbuf for ARP request.\n")); - } + return result; } + +/** + * Send an ARP request packet asking for ipaddr. + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +err_t +etharp_request(struct netif *netif, struct ip_addr *ipaddr) +{ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n")); + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, + ipaddr, ARP_REQUEST); +} + +/** + * Process received ethernet frames. Using this function instead of directly + * calling ip_input and passing ARP frames through etharp in ethernetif_input, + * the ARP cache is protected from concurrent access. + * + * @param p the recevied packet, p->payload pointing to the ethernet header + * @param netif the network interface on which the packet was received + */ +err_t +ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr* ethhdr; + u16_t type; + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%02x:%02x:%02x:%02x:%02x:%02x, src:%02x:%02x:%02x:%02x:%02x:%02x, type:%2hx\n", + (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], + (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], + (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], + (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], + (unsigned)htons(ethhdr->type))); + + type = htons(ethhdr->type); +#if ETHARP_SUPPORT_VLAN + if (type == ETHTYPE_VLAN) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); +#ifdef ETHARP_VLAN_CHECK /* if not, allow all VLANs */ + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* ETHARP_VLAN_CHECK */ + type = htons(vlan->tpid); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + switch (type) { + /* IP packet? */ + case ETHTYPE_IP: +#if ETHARP_TRUST_IP_MAC + /* update ARP table */ + etharp_ip_input(netif, p); +#endif /* ETHARP_TRUST_IP_MAC */ + /* skip Ethernet header */ + if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) { + LWIP_ASSERT("Can't move over header in packet", 0); + pbuf_free(p); + p = NULL; + } else { + /* pass to IP layer */ + ip_input(p, netif); + } + break; + + case ETHTYPE_ARP: + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); + break; + +#if PPPOE_SUPPORT + case ETHTYPE_PPPOEDISC: /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case ETHTYPE_PPPOE: /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif /* PPPOE_SUPPORT */ + + default: + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + p = NULL; + break; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; +} +#endif /* LWIP_ARP */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ethernetif.c b/packages/net/lwip_tcpip/current/src/netif/ethernetif.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/netif/ethernetif.c @@ -0,0 +1,318 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * + */ + +/* + * This file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" + +#if 0 /* don't build, this is only a skeleton, see previous comment */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include +#include +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** + * Helper struct to hold private data used to operate your ethernet interface. + * Keeping the ethernet address of the MAC in this struct is not necessary + * as it is already kept in the struct netif. + * But this is only an example, anyway... + */ +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +/* Forward declarations. */ +static void ethernetif_input(struct netif *netif); + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void +low_level_init(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = ; + ... + netif->hwaddr[5] = ; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* Do whatever else is needed to initialize interface. */ +} + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + + initiate transfer(); + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + for(q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + send data from(q->payload, q->len); + } + + signal that packet should be sent(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * +low_level_input(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p, *q; + u16_t len; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + len = ; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) { + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. + * This does not necessarily have to be a memcpy, you can also preallocate + * pbufs for a DMA-enabled MAC and after receiving truncate it to the + * actually received size. In this case, ensure the tot_len member of the + * pbuf is the sum of the chained pbuf len members. + */ + read data into(q->payload, q->len); + } + acknowledge that packet has been read(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } else { + drop packet(); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +static void +ethernetif_input(struct netif *netif) +{ + struct ethernetif *ethernetif; + struct eth_hdr *ethhdr; + struct pbuf *p; + + ethernetif = netif->state; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + /* IP or ARP packet? */ + case ETHTYPE_IP: + case ETHTYPE_ARP: +#if PPPOE_SUPPORT + /* PPPoE packet? */ + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: +#endif /* PPPOE_SUPPORT */ + /* full packet send to tcpip_thread to process */ + if (netif->input(p, netif)!=ERR_OK) + { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t +ethernetif_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + if (ethernetif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS); + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +#endif /* 0 */ diff --git a/packages/net/lwip_tcpip/current/src/netif/loopif.c b/packages/net/lwip_tcpip/current/src/netif/loopif.c --- a/packages/net/lwip_tcpip/current/src/netif/loopif.c +++ b/packages/net/lwip_tcpip/current/src/netif/loopif.c @@ -1,3 +1,9 @@ +/** + * @file + * Loop Interface + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -34,86 +40,27 @@ #if LWIP_HAVE_LOOPIF #include "netif/loopif.h" -#include "lwip/mem.h" - -#if defined(LWIP_DEBUG) && defined(LWIP_TCPDUMP) -#include "netif/tcpdump.h" -#endif /* LWIP_DEBUG && LWIP_TCPDUMP */ - -#include "lwip/tcp.h" -#include "lwip/ip.h" - -static void -loopif_input( void * arg ) -{ - struct netif *netif = (struct netif *)( ((void **)arg)[ 0 ] ); - struct pbuf *r = (struct pbuf *)( ((void **)arg)[ 1 ] ); - - mem_free( arg ); - netif -> input( r, netif ); -} - -static err_t -loopif_output(struct netif *netif, struct pbuf *p, - struct ip_addr *ipaddr) -{ - struct pbuf *q, *r; - u8_t *ptr; - void **arg; +#include "lwip/snmp.h" -#if defined(LWIP_DEBUG) && defined(LWIP_TCPDUMP) - tcpdump(p); -#endif /* LWIP_DEBUG && LWIP_TCPDUMP */ - - r = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); - if (r != NULL) { - ptr = r->payload; - - for(q = p; q != NULL; q = q->next) { - memcpy(ptr, q->payload, q->len); - ptr += q->len; - } - - arg = mem_malloc( sizeof( void *[2])); - if( NULL == arg ) { - return ERR_MEM; - } - - arg[0] = netif; - arg[1] = r; - /** - * workaround (patch #1779) to try to prevent bug #2595: - * When connecting to "localhost" with the loopif interface, - * tcp_output doesn't get the opportunity to finnish sending the - * segment before tcp_process gets it, resulting in tcp_process - * referencing pcb->unacked-> which still is NULL. - * - * TODO: Is there still a race condition here? Leon - */ - sys_timeout( 1, loopif_input, arg ); - - return ERR_OK; - } - return ERR_MEM; -} - +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ err_t loopif_init(struct netif *netif) { + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0); + netif->name[0] = 'l'; netif->name[1] = 'o'; -#if 0 /** TODO: I think this should be enabled, or not? Leon */ - netif->input = loopif_input; -#endif - netif->output = loopif_output; + netif->output = netif_loop_output; return ERR_OK; } #endif /* LWIP_HAVE_LOOPIF */ - - - - - - - diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/auth.c b/packages/net/lwip_tcpip/current/src/netif/ppp/auth.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/auth.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/auth.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -64,8 +64,14 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#include "ppp.h" -#if PPP_SUPPORT > 0 +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" +#include "timesys.h" + #include "fsm.h" #include "lcp.h" #include "pap.h" @@ -73,12 +79,9 @@ #include "auth.h" #include "ipcp.h" -#if CBCP_SUPPORT > 0 +#if CBCP_SUPPORT #include "cbcp.h" -#endif - -#include "pppdebug.h" - +#endif /* CBCP_SUPPORT */ /*************************/ /*** LOCAL DEFINITIONS ***/ @@ -86,23 +89,21 @@ /* Bits in auth_pending[] */ #define PAP_WITHPEER 1 -#define PAP_PEER 2 +#define PAP_PEER 2 #define CHAP_WITHPEER 4 -#define CHAP_PEER 8 +#define CHAP_PEER 8 - /************************/ /*** LOCAL DATA TYPES ***/ /************************/ /* Used for storing a sequence of words. Usually malloced. */ struct wordlist { - struct wordlist *next; - char word[1]; + struct wordlist *next; + char word[1]; }; - /***********************************/ /*** LOCAL FUNCTION DECLARATIONS ***/ /***********************************/ @@ -122,13 +123,13 @@ static int get_pap_passwd (int, char *, static int have_pap_secret (void); static int have_chap_secret (char *, char *, u32_t); static int ip_addr_check (u32_t, struct wordlist *); -#if 0 /* PAP_SUPPORT > 0 || CHAP_SUPPORT > 0 */ +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ static void set_allowed_addrs(int unit, struct wordlist *addrs); static void free_wordlist (struct wordlist *); -#endif -#if CBCP_SUPPORT > 0 +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ +#if CBCP_SUPPORT static void callback_phase (int); -#endif +#endif /* CBCP_SUPPORT */ /******************************/ @@ -139,10 +140,10 @@ static void callback_phase (int); /*****************************/ /*** LOCAL DATA STRUCTURES ***/ /*****************************/ -#if PAP_SUPPORT > 0 || CHAP_SUPPORT > 0 +#if PAP_SUPPORT || CHAP_SUPPORT /* The name by which the peer authenticated itself to us. */ static char peer_authname[MAXNAMELEN]; -#endif +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ /* Records which authentication operations haven't completed yet. */ static int auth_pending[NUM_PPP]; @@ -162,11 +163,10 @@ static int num_np_open; /* Number of network protocols which have come up. */ static int num_np_up; -#if PAP_SUPPORT > 0 || CHAP_SUPPORT > 0 +#if PAP_SUPPORT || CHAP_SUPPORT /* Set if we got the contents of passwd[] from the pap-secrets file. */ static int passwd_from_file; -#endif - +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ /***********************************/ @@ -176,290 +176,327 @@ static int passwd_from_file; * An Open on LCP has requested a change from Dead to Establish phase. * Do what's necessary to bring the physical layer up. */ -void link_required(int unit) +void +link_required(int unit) { - AUTHDEBUG((LOG_INFO, "link_required: %d\n", unit)); + LWIP_UNUSED_ARG(unit); + + AUTHDEBUG((LOG_INFO, "link_required: %d\n", unit)); } /* * LCP has terminated the link; go to the Dead phase and take the * physical layer down. */ -void link_terminated(int unit) +void +link_terminated(int unit) { - AUTHDEBUG((LOG_INFO, "link_terminated: %d\n", unit)); - - if (lcp_phase[unit] == PHASE_DEAD) - return; - if (logged_in) - logout(); - lcp_phase[unit] = PHASE_DEAD; - AUTHDEBUG((LOG_NOTICE, "Connection terminated.\n")); - pppMainWakeup(unit); + AUTHDEBUG((LOG_INFO, "link_terminated: %d\n", unit)); + if (lcp_phase[unit] == PHASE_DEAD) { + return; + } + if (logged_in) { + logout(); + } + lcp_phase[unit] = PHASE_DEAD; + AUTHDEBUG((LOG_NOTICE, "Connection terminated.\n")); + ppp_link_terminated(unit); } /* * LCP has gone down; it will either die or try to re-establish. */ -void link_down(int unit) +void +link_down(int unit) { - int i; - struct protent *protp; - - AUTHDEBUG((LOG_INFO, "link_down: %d\n", unit)); - if (did_authup) { - /* XXX Do link down processing. */ - did_authup = 0; + int i; + struct protent *protp; + + AUTHDEBUG((LOG_INFO, "link_down: %d\n", unit)); + if (did_authup) { + /* XXX Do link down processing. */ + did_authup = 0; + } + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) { + continue; } - for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { - if (!protp->enabled_flag) - continue; - if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) - (*protp->lowerdown)(unit); - if (protp->protocol < 0xC000 && protp->close != NULL) - (*protp->close)(unit, "LCP down"); + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) { + (*protp->lowerdown)(unit); + } + if (protp->protocol < 0xC000 && protp->close != NULL) { + (*protp->close)(unit, "LCP down"); } - num_np_open = 0; - num_np_up = 0; - if (lcp_phase[unit] != PHASE_DEAD) - lcp_phase[unit] = PHASE_TERMINATE; - pppMainWakeup(unit); + } + num_np_open = 0; + num_np_up = 0; + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + ppp_link_down(unit); } /* * The link is established. * Proceed to the Dead, Authenticate or Network phase as appropriate. */ -void link_established(int unit) +void +link_established(int unit) { - int auth; - int i; - struct protent *protp; - lcp_options *wo = &lcp_wantoptions[unit]; - lcp_options *go = &lcp_gotoptions[unit]; -#if PAP_SUPPORT > 0 || CHAP_SUPPORT > 0 - lcp_options *ho = &lcp_hisoptions[unit]; -#endif - - AUTHDEBUG((LOG_INFO, "link_established: %d\n", unit)); + int auth; + int i; + struct protent *protp; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; +#if PAP_SUPPORT || CHAP_SUPPORT + lcp_options *ho = &lcp_hisoptions[unit]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + AUTHDEBUG((LOG_INFO, "link_established: %d\n", unit)); + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) { + (*protp->lowerup)(unit); + } + } + if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) { /* - * Tell higher-level protocols that LCP is up. + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. */ - for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) - if (protp->protocol != PPP_LCP && protp->enabled_flag - && protp->lowerup != NULL) - (*protp->lowerup)(unit); - - if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) { - /* - * We wanted the peer to authenticate itself, and it refused: - * treat it as though it authenticated with PAP using a username - * of "" and a password of "". If that's not OK, boot it out. - */ - if (!wo->neg_upap || !null_login(unit)) { - AUTHDEBUG((LOG_WARNING, "peer refused to authenticate\n")); - lcp_close(unit, "peer refused to authenticate"); - return; - } + if (!wo->neg_upap || !null_login(unit)) { + AUTHDEBUG((LOG_WARNING, "peer refused to authenticate\n")); + lcp_close(unit, "peer refused to authenticate"); + return; } - - lcp_phase[unit] = PHASE_AUTHENTICATE; - auth = 0; -#if CHAP_SUPPORT > 0 - if (go->neg_chap) { - ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype); - auth |= CHAP_PEER; - } -#endif -#if PAP_SUPPORT > 0 && CHAP_SUPPORT > 0 - else -#endif -#if PAP_SUPPORT > 0 - if (go->neg_upap) { - upap_authpeer(unit); - auth |= PAP_PEER; + } + + lcp_phase[unit] = PHASE_AUTHENTICATE; + auth = 0; +#if CHAP_SUPPORT + if (go->neg_chap) { + chap_auth_peer(unit, ppp_settings.our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + chap_auth_with_peer(unit, ppp_settings.user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + if (ppp_settings.passwd[0] == 0) { + passwd_from_file = 1; + if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) { + AUTHDEBUG((LOG_ERR, "No secret found for PAP login\n")); + } } -#endif -#if CHAP_SUPPORT > 0 - if (ho->neg_chap) { - ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype); - auth |= CHAP_WITHPEER; - } -#endif -#if PAP_SUPPORT > 0 && CHAP_SUPPORT > 0 - else -#endif -#if PAP_SUPPORT > 0 - if (ho->neg_upap) { - if (ppp_settings.passwd[0] == 0) { - passwd_from_file = 1; - if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) - AUTHDEBUG((LOG_ERR, "No secret found for PAP login\n")); - } - upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd); - auth |= PAP_WITHPEER; - } -#endif - auth_pending[unit] = auth; - - if (!auth) - network_phase(unit); + upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd); + auth |= PAP_WITHPEER; + } +#endif /* PAP_SUPPORT */ + auth_pending[unit] = auth; + + if (!auth) { + network_phase(unit); + } } - /* * The peer has failed to authenticate himself using `protocol'. */ -void auth_peer_fail(int unit, u16_t protocol) +void +auth_peer_fail(int unit, u16_t protocol) { - AUTHDEBUG((LOG_INFO, "auth_peer_fail: %d proto=%X\n", unit, protocol)); - /* - * Authentication failure: take the link down - */ - lcp_close(unit, "Authentication failed"); + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG((LOG_INFO, "auth_peer_fail: %d proto=%X\n", unit, protocol)); + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); } -#if PAP_SUPPORT > 0 || CHAP_SUPPORT > 0 +#if PAP_SUPPORT || CHAP_SUPPORT /* * The peer has been successfully authenticated using `protocol'. */ -void auth_peer_success(int unit, u16_t protocol, char *name, int namelen) +void +auth_peer_success(int unit, u16_t protocol, char *name, int namelen) { - int pbit; - - AUTHDEBUG((LOG_INFO, "auth_peer_success: %d proto=%X\n", unit, protocol)); - switch (protocol) { + int pbit; + + AUTHDEBUG((LOG_INFO, "auth_peer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { case PPP_CHAP: - pbit = CHAP_PEER; - break; + pbit = CHAP_PEER; + break; case PPP_PAP: - pbit = PAP_PEER; - break; + pbit = PAP_PEER; + break; default: - AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", - protocol)); - return; - } - - /* - * Save the authenticated name of the peer for later. - */ - if (namelen > sizeof(peer_authname) - 1) - namelen = sizeof(peer_authname) - 1; - BCOPY(name, peer_authname, namelen); - peer_authname[namelen] = 0; - - /* - * If there is no more authentication still to be done, - * proceed to the network (or callback) phase. - */ - if ((auth_pending[unit] &= ~pbit) == 0) - network_phase(unit); + AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol)); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > sizeof(peer_authname) - 1) { + namelen = sizeof(peer_authname) - 1; + } + BCOPY(name, peer_authname, namelen); + peer_authname[namelen] = 0; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } } /* * We have failed to authenticate ourselves to the peer using `protocol'. */ -void auth_withpeer_fail(int unit, u16_t protocol) +void +auth_withpeer_fail(int unit, u16_t protocol) { - int errCode = PPPERR_AUTHFAIL; - - AUTHDEBUG((LOG_INFO, "auth_withpeer_fail: %d proto=%X\n", unit, protocol)); - if (passwd_from_file) - BZERO(ppp_settings.passwd, MAXSECRETLEN); - /* - * XXX Warning: the unit number indicates the interface which is - * not necessarily the PPP connection. It works here as long - * as we are only supporting PPP interfaces. - */ - pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode); + int errCode = PPPERR_AUTHFAIL; + + LWIP_UNUSED_ARG(protocol); - /* - * We've failed to authenticate ourselves to our peer. - * He'll probably take the link down, and there's not much - * we can do except wait for that. - */ + AUTHDEBUG((LOG_INFO, "auth_withpeer_fail: %d proto=%X\n", unit, protocol)); + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + /* + * XXX Warning: the unit number indicates the interface which is + * not necessarily the PPP connection. It works here as long + * as we are only supporting PPP interfaces. + */ + ppp_ioctl(unit, PPPCTLS_ERRCODE, &errCode); + + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ } /* * We have successfully authenticated ourselves with the peer using `protocol'. */ -void auth_withpeer_success(int unit, u16_t protocol) +void +auth_withpeer_success(int unit, u16_t protocol) { - int pbit; - - AUTHDEBUG((LOG_INFO, "auth_withpeer_success: %d proto=%X\n", unit, protocol)); - switch (protocol) { + int pbit; + + AUTHDEBUG((LOG_INFO, "auth_withpeer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { case PPP_CHAP: - pbit = CHAP_WITHPEER; - break; + pbit = CHAP_WITHPEER; + break; case PPP_PAP: - if (passwd_from_file) - BZERO(ppp_settings.passwd, MAXSECRETLEN); - pbit = PAP_WITHPEER; - break; + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + pbit = PAP_WITHPEER; + break; default: - AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", - protocol)); - pbit = 0; - } - - /* - * If there is no more authentication still being done, - * proceed to the network (or callback) phase. - */ - if ((auth_pending[unit] &= ~pbit) == 0) - network_phase(unit); + AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol)); + pbit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } } -#endif +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ /* * np_up - a network protocol has come up. */ -void np_up(int unit, u16_t proto) +void +np_up(int unit, u16_t proto) { - AUTHDEBUG((LOG_INFO, "np_up: %d proto=%X\n", unit, proto)); - if (num_np_up == 0) { - AUTHDEBUG((LOG_INFO, "np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit)); - /* - * At this point we consider that the link has come up successfully. - */ - if (ppp_settings.idle_time_limit > 0) - TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit); - - /* - * Set a timeout to close the connection once the maximum - * connect time has expired. - */ - if (ppp_settings.maxconnect > 0) - TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect); + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_up: %d proto=%X\n", unit, proto)); + if (num_np_up == 0) { + AUTHDEBUG((LOG_INFO, "np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit)); + /* + * At this point we consider that the link has come up successfully. + */ + if (ppp_settings.idle_time_limit > 0) { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit); } - ++num_np_up; + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (ppp_settings.maxconnect > 0) { + TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect); + } + } + ++num_np_up; } /* * np_down - a network protocol has gone down. */ -void np_down(int unit, u16_t proto) +void +np_down(int unit, u16_t proto) { - AUTHDEBUG((LOG_INFO, "np_down: %d proto=%X\n", unit, proto)); - if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) { - UNTIMEOUT(check_idle, NULL); - } + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_down: %d proto=%X\n", unit, proto)); + if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } } /* * np_finished - a network protocol has finished using the link. */ -void np_finished(int unit, u16_t proto) +void +np_finished(int unit, u16_t proto) { - AUTHDEBUG((LOG_INFO, "np_finished: %d proto=%X\n", unit, proto)); - if (--num_np_open <= 0) { - /* no further use for the link: shut up shop. */ - lcp_close(0, "No network protocols running"); - } + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_finished: %d proto=%X\n", unit, proto)); + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } } /* @@ -467,28 +504,30 @@ void np_finished(int unit, u16_t proto) * authentication options, i.e. whether we have appropriate secrets * to use for authenticating ourselves and/or the peer. */ -void auth_reset(int unit) +void +auth_reset(int unit) { - lcp_options *go = &lcp_gotoptions[unit]; - lcp_options *ao = &lcp_allowoptions[0]; - ipcp_options *ipwo = &ipcp_wantoptions[0]; - u32_t remote; - - AUTHDEBUG((LOG_INFO, "auth_reset: %d\n", unit)); - ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL)); - ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/; - - if (go->neg_upap && !have_pap_secret()) - go->neg_upap = 0; - if (go->neg_chap) { - remote = ipwo->accept_remote? 0: ipwo->hisaddr; - if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) - go->neg_chap = 0; + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + AUTHDEBUG((LOG_INFO, "auth_reset: %d\n", unit)); + ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL)); + ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/; + + if (go->neg_upap && !have_pap_secret()) { + go->neg_upap = 0; + } + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) { + go->neg_chap = 0; } + } } - -#if PAP_SUPPORT > 0 +#if PAP_SUPPORT /* * check_passwd - Check the user name and passwd against the PAP secrets * file. If requested, also check against the system password database, @@ -499,81 +538,83 @@ void auth_reset(int unit) * UPAP_AUTHACK: Authentication succeeded. * In either case, msg points to an appropriate message. */ -int check_passwd( - int unit, - char *auser, - int userlen, - char *apasswd, - int passwdlen, - char **msg, - int *msglen -) +int +check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen) { #if 1 - *msg = (char *) 0; - return UPAP_AUTHACK; /* XXX Assume all entries OK. */ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(auser); + LWIP_UNUSED_ARG(userlen); + LWIP_UNUSED_ARG(apasswd); + LWIP_UNUSED_ARG(passwdlen); + LWIP_UNUSED_ARG(msglen); + *msg = (char *) 0; + return UPAP_AUTHACK; /* XXX Assume all entries OK. */ #else - int ret = 0; - struct wordlist *addrs = NULL; - char passwd[256], user[256]; - char secret[MAXWORDLEN]; - static u_short attempts = 0; - - /* - * Make copies of apasswd and auser, then null-terminate them. - */ - BCOPY(apasswd, passwd, passwdlen); - passwd[passwdlen] = '\0'; - BCOPY(auser, user, userlen); - user[userlen] = '\0'; - *msg = (char *) 0; + int ret = 0; + struct wordlist *addrs = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static u16_t attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + */ + BCOPY(apasswd, passwd, passwdlen); + passwd[passwdlen] = '\0'; + BCOPY(auser, user, userlen); + user[userlen] = '\0'; + *msg = (char *) 0; + + /* XXX Validate user name and password. */ + ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */ - /* XXX Validate user name and password. */ - ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */ - - if (ret == UPAP_AUTHNAK) { - if (*msg == (char *) 0) - *msg = "Login incorrect"; - *msglen = strlen(*msg); - /* - * Frustrate passwd stealer programs. - * Allow 10 tries, but start backing off after 3 (stolen from login). - * On 10'th, drop the connection. - */ - if (attempts++ >= 10) { - AUTHDEBUG((LOG_WARNING, "%d LOGIN FAILURES BY %s\n", attempts, user)); - /*ppp_panic("Excess Bad Logins");*/ - } - if (attempts > 3) { - sys_msleep((attempts - 3) * 5); - } - if (addrs != NULL) { - free_wordlist(addrs); - } - } else { - attempts = 0; /* Reset count */ - if (*msg == (char *) 0) - *msg = "Login ok"; - *msglen = strlen(*msg); - set_allowed_addrs(unit, addrs); + if (ret == UPAP_AUTHNAK) { + if (*msg == (char *) 0) { + *msg = "Login incorrect"; + } + *msglen = strlen(*msg); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + AUTHDEBUG((LOG_WARNING, "%d LOGIN FAILURES BY %s\n", attempts, user)); + /*ppp_panic("Excess Bad Logins");*/ + } + if (attempts > 3) { + sys_msleep((attempts - 3) * 5); } - - BZERO(passwd, sizeof(passwd)); - BZERO(secret, sizeof(secret)); - - return ret; + if (addrs != NULL) { + free_wordlist(addrs); + } + } else { + attempts = 0; /* Reset count */ + if (*msg == (char *) 0) { + *msg = "Login ok"; + } + *msglen = strlen(*msg); + set_allowed_addrs(unit, addrs); + } + + BZERO(passwd, sizeof(passwd)); + BZERO(secret, sizeof(secret)); + + return ret; #endif } -#endif +#endif /* PAP_SUPPORT */ /* * auth_ip_addr - check whether the peer is authorized to use * a given IP address. Returns 1 if authorized, 0 otherwise. */ -int auth_ip_addr(int unit, u32_t addr) +int +auth_ip_addr(int unit, u32_t addr) { - return ip_addr_check(addr, addresses[unit]); + return ip_addr_check(addr, addresses[unit]); } /* @@ -581,114 +622,120 @@ int auth_ip_addr(int unit, u32_t addr) * to use, such as an address in the loopback net or a multicast address. * addr is in network byte order. */ -int bad_ip_adrs(u32_t addr) +int +bad_ip_adrs(u32_t addr) { - addr = ntohl(addr); - return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET - || IN_MULTICAST(addr) || IN_BADCLASS(addr); + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); } -#if CHAP_SUPPORT > 0 +#if CHAP_SUPPORT /* * get_secret - open the CHAP secret file and return the secret * for authenticating the given client on the given server. * (We could be either client or server). */ -int get_secret( - int unit, - char *client, - char *server, - char *secret, - int *secret_len, - int save_addrs -) +int get_secret( int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs) { #if 1 - int len; - struct wordlist *addrs; - - addrs = NULL; + int len; + struct wordlist *addrs; - if(!client || !client[0] || strcmp(client, ppp_settings.user)) { - return 0; - } + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(save_addrs); + + addrs = NULL; - len = strlen(ppp_settings.passwd); - if (len > MAXSECRETLEN) { - AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); - len = MAXSECRETLEN; - } - BCOPY(ppp_settings.passwd, secret, len); - *secret_len = len; - - return 1; + if(!client || !client[0] || strcmp(client, ppp_settings.user)) { + return 0; + } + + len = strlen(ppp_settings.passwd); + if (len > MAXSECRETLEN) { + AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(ppp_settings.passwd, secret, len); + *secret_len = len; + + return 1; #else - int ret = 0, len; - struct wordlist *addrs; - char secbuf[MAXWORDLEN]; - - addrs = NULL; - secbuf[0] = 0; + int ret = 0, len; + struct wordlist *addrs; + char secbuf[MAXWORDLEN]; + + addrs = NULL; + secbuf[0] = 0; + + /* XXX Find secret. */ + if (ret < 0) { + return 0; + } - /* XXX Find secret. */ - if (ret < 0) - return 0; - - if (save_addrs) - set_allowed_addrs(unit, addrs); - - len = strlen(secbuf); - if (len > MAXSECRETLEN) { - AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); - len = MAXSECRETLEN; - } - BCOPY(secbuf, secret, len); - BZERO(secbuf, sizeof(secbuf)); - *secret_len = len; - - return 1; + if (save_addrs) { + set_allowed_addrs(unit, addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(secbuf, secret, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; #endif } -#endif +#endif /* CHAP_SUPPORT */ #if 0 /* UNUSED */ /* * auth_check_options - called to check authentication options. */ -void auth_check_options(void) +void +auth_check_options(void) { - lcp_options *wo = &lcp_wantoptions[0]; - int can_auth; - ipcp_options *ipwo = &ipcp_wantoptions[0]; - u32_t remote; - - /* Default our_name to hostname, and user to our_name */ - if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) - strcpy(ppp_settings.our_name, ppp_settings.hostname); - if (ppp_settings.user[0] == 0) - strcpy(ppp_settings.user, ppp_settings.our_name); - - /* If authentication is required, ask peer for CHAP or PAP. */ - if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) { - wo->neg_chap = 1; - wo->neg_upap = 1; - } - - /* - * Check whether we have appropriate secrets to use - * to authenticate the peer. - */ - can_auth = wo->neg_upap && have_pap_secret(); - if (!can_auth && wo->neg_chap) { - remote = ipwo->accept_remote? 0: ipwo->hisaddr; - can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote); - } - - if (ppp_settings.auth_required && !can_auth) { - ppp_panic("No auth secret"); - } + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) { + strcpy(ppp_settings.our_name, ppp_settings.hostname); + } + + if (ppp_settings.user[0] == 0) { + strcpy(ppp_settings.user, ppp_settings.our_name); + } + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && have_pap_secret(); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote); + } + + if (ppp_settings.auth_required && !can_auth) { + ppp_panic("No auth secret"); + } } #endif @@ -699,76 +746,82 @@ void auth_check_options(void) /* * Proceed to the network phase. */ -static void network_phase(int unit) +static void +network_phase(int unit) { - int i; - struct protent *protp; - lcp_options *go = &lcp_gotoptions[unit]; - - /* - * If the peer had to authenticate, run the auth-up script now. - */ - if ((go->neg_chap || go->neg_upap) && !did_authup) { - /* XXX Do setup for peer authentication. */ - did_authup = 1; + int i; + struct protent *protp; + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if ((go->neg_chap || go->neg_upap) && !did_authup) { + /* XXX Do setup for peer authentication. */ + did_authup = 1; + } + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + lcp_phase[unit] = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif /* CBCP_SUPPORT */ + + lcp_phase[unit] = PHASE_NETWORK; + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) { + ++num_np_open; + } } - -#if CBCP_SUPPORT > 0 - /* - * If we negotiated callback, do it now. - */ - if (go->neg_cbcp) { - lcp_phase[unit] = PHASE_CALLBACK; - (*cbcp_protent.open)(unit); - return; - } -#endif - - lcp_phase[unit] = PHASE_NETWORK; - for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) - if (protp->protocol < 0xC000 && protp->enabled_flag - && protp->open != NULL) { - (*protp->open)(unit); - if (protp->protocol != PPP_CCP) - ++num_np_open; - } - - if (num_np_open == 0) - /* nothing to do */ - lcp_close(0, "No network protocols running"); + } + + if (num_np_open == 0) { + /* nothing to do */ + lcp_close(0, "No network protocols running"); + } } /* * check_idle - check whether the link has been idle for long * enough that we can shut it down. */ -static void check_idle(void *arg) +static void +check_idle(void *arg) { - struct ppp_idle idle; - u_short itime; - - (void)arg; - if (!get_idle_time(0, &idle)) - return; - itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); - if (itime >= ppp_settings.idle_time_limit) { - /* link is idle: shut it down. */ - AUTHDEBUG((LOG_INFO, "Terminating connection due to lack of activity.\n")); - lcp_close(0, "Link inactive"); - } else { - TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime); - } + struct ppp_idle idle; + u16_t itime; + + LWIP_UNUSED_ARG(arg); + if (!get_idle_time(0, &idle)) { + return; + } + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= ppp_settings.idle_time_limit) { + /* link is idle: shut it down. */ + AUTHDEBUG((LOG_INFO, "Terminating connection due to lack of activity.\n")); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime); + } } /* * connect_time_expired - log a message and close the connection. */ -static void connect_time_expired(void *arg) +static void +connect_time_expired(void *arg) { - (void)arg; + LWIP_UNUSED_ARG(arg); - AUTHDEBUG((LOG_INFO, "Connect time expired\n")); - lcp_close(0, "Connect time expired"); /* Close connection */ + AUTHDEBUG((LOG_INFO, "Connect time expired\n")); + lcp_close(0, "Connect time expired"); /* Close connection */ } #if 0 @@ -781,147 +834,156 @@ static void connect_time_expired(void *a * UPAP_AUTHACK: Login succeeded. * In either case, msg points to an appropriate message. */ -static int login(char *user, char *passwd, char **msg, int *msglen) +static int +login(char *user, char *passwd, char **msg, int *msglen) { - /* XXX Fail until we decide that we want to support logins. */ - return (UPAP_AUTHNAK); + /* XXX Fail until we decide that we want to support logins. */ + return (UPAP_AUTHNAK); } #endif /* * logout - Logout the user. */ -static void logout(void) +static void +logout(void) { - logged_in = 0; + logged_in = 0; } - /* * null_login - Check if a username of "" and a password of "" are * acceptable, and iff so, set the list of acceptable IP addresses * and return 1. */ -static int null_login(int unit) +static int +null_login(int unit) { - (void)unit; - /* XXX Fail until we decide that we want to support logins. */ - return 0; + LWIP_UNUSED_ARG(unit); + /* XXX Fail until we decide that we want to support logins. */ + return 0; } - /* * get_pap_passwd - get a password for authenticating ourselves with * our peer using PAP. Returns 1 on success, 0 if no suitable password * could be found. */ -static int get_pap_passwd(int unit, char *user, char *passwd) +static int +get_pap_passwd(int unit, char *user, char *passwd) { + LWIP_UNUSED_ARG(unit); /* normally we would reject PAP if no password is provided, but this causes problems with some providers (like CHT in Taiwan) who incorrectly request PAP and expect a bogus/empty password, so always provide a default user/passwd of "none"/"none" */ - if(user) - strcpy(user, "none"); - if(passwd) - strcpy(passwd, "none"); - - return 1; + if(user) { + strcpy(user, "none"); + } + if(passwd) { + strcpy(passwd, "none"); + } + return 1; } - /* * have_pap_secret - check whether we have a PAP file with any * secrets that we could possibly use for authenticating the peer. */ -static int have_pap_secret(void) +static int +have_pap_secret(void) { - /* XXX Fail until we set up our passwords. */ - return 0; + /* XXX Fail until we set up our passwords. */ + return 0; } - /* * have_chap_secret - check whether we have a CHAP file with a * secret that we could possibly use for authenticating `client' * on `server'. Either can be the null string, meaning we don't * know the identity yet. */ -static int have_chap_secret(char *client, char *server, u32_t remote) +static int +have_chap_secret(char *client, char *server, u32_t remote) { - (void)client; - (void)server; - (void)remote; - /* XXX Fail until we set up our passwords. */ - return 0; + LWIP_UNUSED_ARG(client); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(remote); + /* XXX Fail until we set up our passwords. */ + return 0; } - -#if 0 /* PAP_SUPPORT > 0 || CHAP_SUPPORT > 0 */ +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ /* * set_allowed_addrs() - set the list of allowed addresses. */ -static void set_allowed_addrs(int unit, struct wordlist *addrs) +static void +set_allowed_addrs(int unit, struct wordlist *addrs) { - if (addresses[unit] != NULL) - free_wordlist(addresses[unit]); - addresses[unit] = addrs; + if (addresses[unit] != NULL) { + free_wordlist(addresses[unit]); + } + addresses[unit] = addrs; #if 0 - /* - * If there's only one authorized address we might as well - * ask our peer for that one right away - */ - if (addrs != NULL && addrs->next == NULL) { - char *p = addrs->word; - struct ipcp_options *wo = &ipcp_wantoptions[unit]; - u32_t a; - struct hostent *hp; - - if (wo->hisaddr == 0 && *p != '!' && *p != '-' - && strchr(p, '/') == NULL) { - hp = gethostbyname(p); - if (hp != NULL && hp->h_addrtype == AF_INET) - a = *(u32_t *)hp->h_addr; - else - a = inet_addr(p); - if (a != (u32_t) -1) - wo->hisaddr = a; - } + /* + * If there's only one authorized address we might as well + * ask our peer for that one right away + */ + if (addrs != NULL && addrs->next == NULL) { + char *p = addrs->word; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t a; + struct hostent *hp; + + if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) { + hp = gethostbyname(p); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + a = inet_addr(p); + } + if (a != (u32_t) -1) { + wo->hisaddr = a; + } } + } #endif } -#endif +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ -static int ip_addr_check(u32_t addr, struct wordlist *addrs) +static int +ip_addr_check(u32_t addr, struct wordlist *addrs) { - - /* don't allow loopback or multicast address */ - if (bad_ip_adrs(addr)) - return 0; - - if (addrs == NULL) - return !ppp_settings.auth_required; /* no addresses authorized */ - - /* XXX All other addresses allowed. */ - return 1; + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) { + return 0; + } + + if (addrs == NULL) { + return !ppp_settings.auth_required; /* no addresses authorized */ + } + + /* XXX All other addresses allowed. */ + return 1; } -#if 0 /* PAP_SUPPORT > 0 || CHAP_SUPPORT */ +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ /* * free_wordlist - release memory allocated for a wordlist. */ -static void free_wordlist(struct wordlist *wp) +static void +free_wordlist(struct wordlist *wp) { - struct wordlist *next; - - while (wp != NULL) { - next = wp->next; - free(wp); - wp = next; - } + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } } -#endif +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ #endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/auth.h b/packages/net/lwip_tcpip/current/src/netif/ppp/auth.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/auth.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/auth.h @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-04 Guy Lancaster , Global Election Systems Inc. -* Original derived from BSD pppd.h. +* Original derived from BSD pppd.h. *****************************************************************************/ /* * pppd.h - PPP daemon global declarations. @@ -56,14 +56,30 @@ /*********************** *** PUBLIC FUNCTIONS *** ***********************/ -void link_required (int); /* we are starting to use the link */ -void link_terminated (int); /* we are finished with the link */ -void link_down (int); /* the LCP layer has left the Opened state */ -void link_established (int); /* the link is up; authenticate now */ -void np_up (int, u16_t); /* a network protocol has come up */ -void np_down (int, u16_t); /* a network protocol has gone down */ -void np_finished (int, u16_t); /* a network protocol no longer needs link */ -void auth_peer_fail (int, u16_t);/* peer failed to authenticate itself */ + +/* we are starting to use the link */ +void link_required (int); + +/* we are finished with the link */ +void link_terminated (int); + +/* the LCP layer has left the Opened state */ +void link_down (int); + +/* the link is up; authenticate now */ +void link_established (int); + +/* a network protocol has come up */ +void np_up (int, u16_t); + +/* a network protocol has gone down */ +void np_down (int, u16_t); + +/* a network protocol no longer needs link */ +void np_finished (int, u16_t); + +/* peer failed to authenticate itself */ +void auth_peer_fail (int, u16_t); /* peer successfully authenticated itself */ void auth_peer_success (int, u16_t, char *, int); @@ -76,7 +92,9 @@ void auth_withpeer_success (int, u16_t); /* check authentication options supplied */ void auth_check_options (void); -void auth_reset (int); /* check what secrets we have */ + +/* check what secrets we have */ +void auth_reset (int); /* Check peer-supplied username/password */ int check_passwd (int, char *, int, char *, int, char **, int *); @@ -90,5 +108,4 @@ int auth_ip_addr (int, u32_t); /* check if IP address is unreasonable */ int bad_ip_adrs (u32_t); - #endif /* AUTH_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/chap.c b/packages/net/lwip_tcpip/current/src/netif/ppp/chap.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/chap.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/chap.c @@ -8,13 +8,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -29,7 +29,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-04 Guy Lancaster , Global Election Systems Inc. -* Original based on BSD chap.c. +* Original based on BSD chap.c. *****************************************************************************/ /* * chap.c - Challenge Handshake Authentication Protocol. @@ -66,18 +66,22 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#include "ppp.h" -#if PPP_SUPPORT > 0 +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" +#include "timesys.h" + #include "magic.h" - -#if CHAP_SUPPORT > 0 - #include "randm.h" #include "auth.h" #include "md5.h" #include "chap.h" #include "chpms.h" -#include "pppdebug.h" /*************************/ @@ -96,777 +100,804 @@ /* * Protocol entry points. */ -static void ChapInit (int); -static void ChapLowerUp (int); -static void ChapLowerDown (int); -static void ChapInput (int, u_char *, int); -static void ChapProtocolReject (int); -static int ChapPrintPkt (u_char *, int, - void (*) (void *, char *, ...), void *); +static void chap_init (int); +static void chap_lowerup (int); +static void chap_lowerdown (int); +static void chap_input (int, u8_t *, int); +static void chap_protrej (int); +#if 0 +static int chap_printpkt (u8_t *, int, void (*) (void *, char *, ...), void *); +#endif -static void ChapChallengeTimeout (void *); -static void ChapResponseTimeout (void *); -static void ChapReceiveChallenge (chap_state *, u_char *, int, int); -static void ChapRechallenge (void *); -static void ChapReceiveResponse (chap_state *, u_char *, int, int); -static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len); -static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len); -static void ChapSendStatus (chap_state *, int); -static void ChapSendChallenge (chap_state *); -static void ChapSendResponse (chap_state *); -static void ChapGenChallenge (chap_state *); +static void chap_challenge_timeout (void *); +static void chap_response_timeout (void *); +static void chap_receive_challenge (chap_state *, u8_t *, int, int); +static void chap_rechallenge (void *); +static void chap_receive_response (chap_state *, u8_t *, int, int); +static void chap_receive_success(chap_state *cstate, u8_t *inp, u8_t id, int len); +static void chap_receive_failure(chap_state *cstate, u8_t *inp, u8_t id, int len); +static void chap_send_status (chap_state *, int); +static void chap_send_challenge (chap_state *); +static void chap_send_response (chap_state *); +static void chap_gen_challenge (chap_state *); /******************************/ /*** PUBLIC DATA STRUCTURES ***/ /******************************/ -chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ struct protent chap_protent = { - PPP_CHAP, - ChapInit, - ChapInput, - ChapProtocolReject, - ChapLowerUp, - ChapLowerDown, - NULL, - NULL, + PPP_CHAP, + chap_init, + chap_input, + chap_protrej, + chap_lowerup, + chap_lowerdown, + NULL, + NULL, #if 0 - ChapPrintPkt, - NULL, + chap_printpkt, + NULL, #endif - 1, - "CHAP", + 1, + "CHAP", #if 0 - NULL, - NULL, - NULL + NULL, + NULL, + NULL #endif }; - -/*****************************/ -/*** LOCAL DATA STRUCTURES ***/ -/*****************************/ -static char *ChapCodenames[] = { - "Challenge", "Response", "Success", "Failure" -}; - - - /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ /***********************************/ /* - * ChapAuthWithPeer - Authenticate us with our peer (start client). + * chap_auth_with_peer - Authenticate us with our peer (start client). * */ -void ChapAuthWithPeer(int unit, char *our_name, int digest) +void +chap_auth_with_peer(int unit, char *our_name, int digest) { - chap_state *cstate = &chap[unit]; - - cstate->resp_name = our_name; - cstate->resp_type = digest; - - if (cstate->clientstate == CHAPCS_INITIAL || - cstate->clientstate == CHAPCS_PENDING) { - /* lower layer isn't up - wait until later */ - cstate->clientstate = CHAPCS_PENDING; - return; - } - - /* - * We get here as a result of LCP coming up. - * So even if CHAP was open before, we will - * have to re-authenticate ourselves. - */ - cstate->clientstate = CHAPCS_LISTEN; + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; } /* - * ChapAuthPeer - Authenticate our peer (start server). + * chap_auth_peer - Authenticate our peer (start server). */ -void ChapAuthPeer(int unit, char *our_name, int digest) +void +chap_auth_peer(int unit, char *our_name, int digest) { - chap_state *cstate = &chap[unit]; - - cstate->chal_name = our_name; - cstate->chal_type = digest; - - if (cstate->serverstate == CHAPSS_INITIAL || - cstate->serverstate == CHAPSS_PENDING) { - /* lower layer isn't up - wait until later */ - cstate->serverstate = CHAPSS_PENDING; - return; - } - - ChapGenChallenge(cstate); - ChapSendChallenge(cstate); /* crank it up dude! */ - cstate->serverstate = CHAPSS_INITIAL_CHAL; + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + chap_gen_challenge(cstate); + chap_send_challenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; } - - /**********************************/ /*** LOCAL FUNCTION DEFINITIONS ***/ /**********************************/ /* - * ChapInit - Initialize a CHAP unit. + * chap_init - Initialize a CHAP unit. */ -static void ChapInit(int unit) +static void +chap_init(int unit) { - chap_state *cstate = &chap[unit]; - - BZERO(cstate, sizeof(*cstate)); - cstate->unit = unit; - cstate->clientstate = CHAPCS_INITIAL; - cstate->serverstate = CHAPSS_INITIAL; - cstate->timeouttime = CHAP_DEFTIMEOUT; - cstate->max_transmits = CHAP_DEFTRANSMITS; - /* random number generator is initialized in magic_init */ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ } /* - * ChapChallengeTimeout - Timeout expired on sending challenge. + * chap_challenge_timeout - Timeout expired on sending challenge. */ -static void ChapChallengeTimeout(void *arg) +static void +chap_challenge_timeout(void *arg) { - chap_state *cstate = (chap_state *) arg; - - /* if we aren't sending challenges, don't worry. then again we */ - /* probably shouldn't be here either */ - if (cstate->serverstate != CHAPSS_INITIAL_CHAL && - cstate->serverstate != CHAPSS_RECHALLENGE) - return; - - if (cstate->chal_transmits >= cstate->max_transmits) { - /* give up on peer */ - CHAPDEBUG((LOG_ERR, "Peer failed to respond to CHAP challenge\n")); - cstate->serverstate = CHAPSS_BADAUTH; - auth_peer_fail(cstate->unit, PPP_CHAP); - return; - } - - ChapSendChallenge(cstate); /* Re-send challenge */ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) { + return; + } + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + CHAPDEBUG((LOG_ERR, "Peer failed to respond to CHAP challenge\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + chap_send_challenge(cstate); /* Re-send challenge */ } /* - * ChapResponseTimeout - Timeout expired on sending response. + * chap_response_timeout - Timeout expired on sending response. */ -static void ChapResponseTimeout(void *arg) +static void +chap_response_timeout(void *arg) { - chap_state *cstate = (chap_state *) arg; - - /* if we aren't sending a response, don't worry. */ - if (cstate->clientstate != CHAPCS_RESPONSE) - return; - - ChapSendResponse(cstate); /* re-send response */ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) { + return; + } + + chap_send_response(cstate); /* re-send response */ } /* - * ChapRechallenge - Time to challenge the peer again. + * chap_rechallenge - Time to challenge the peer again. */ -static void ChapRechallenge(void *arg) +static void +chap_rechallenge(void *arg) { - chap_state *cstate = (chap_state *) arg; - - /* if we aren't sending a response, don't worry. */ - if (cstate->serverstate != CHAPSS_OPEN) - return; - - ChapGenChallenge(cstate); - ChapSendChallenge(cstate); - cstate->serverstate = CHAPSS_RECHALLENGE; + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) { + return; + } + + chap_gen_challenge(cstate); + chap_send_challenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; } /* - * ChapLowerUp - The lower layer is up. + * chap_lowerup - The lower layer is up. * * Start up if we have pending requests. */ -static void ChapLowerUp(int unit) +static void +chap_lowerup(int unit) { - chap_state *cstate = &chap[unit]; - - if (cstate->clientstate == CHAPCS_INITIAL) - cstate->clientstate = CHAPCS_CLOSED; - else if (cstate->clientstate == CHAPCS_PENDING) - cstate->clientstate = CHAPCS_LISTEN; - - if (cstate->serverstate == CHAPSS_INITIAL) - cstate->serverstate = CHAPSS_CLOSED; - else if (cstate->serverstate == CHAPSS_PENDING) { - ChapGenChallenge(cstate); - ChapSendChallenge(cstate); - cstate->serverstate = CHAPSS_INITIAL_CHAL; - } + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) { + cstate->clientstate = CHAPCS_CLOSED; + } else if (cstate->clientstate == CHAPCS_PENDING) { + cstate->clientstate = CHAPCS_LISTEN; + } + + if (cstate->serverstate == CHAPSS_INITIAL) { + cstate->serverstate = CHAPSS_CLOSED; + } else if (cstate->serverstate == CHAPSS_PENDING) { + chap_gen_challenge(cstate); + chap_send_challenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } } /* - * ChapLowerDown - The lower layer is down. + * chap_lowerdown - The lower layer is down. * * Cancel all timeouts. */ -static void ChapLowerDown(int unit) +static void +chap_lowerdown(int unit) { - chap_state *cstate = &chap[unit]; - - /* Timeout(s) pending? Cancel if so. */ - if (cstate->serverstate == CHAPSS_INITIAL_CHAL || - cstate->serverstate == CHAPSS_RECHALLENGE) - UNTIMEOUT(ChapChallengeTimeout, cstate); - else if (cstate->serverstate == CHAPSS_OPEN - && cstate->chal_interval != 0) - UNTIMEOUT(ChapRechallenge, cstate); - if (cstate->clientstate == CHAPCS_RESPONSE) - UNTIMEOUT(ChapResponseTimeout, cstate); - - cstate->clientstate = CHAPCS_INITIAL; - cstate->serverstate = CHAPSS_INITIAL; + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) { + UNTIMEOUT(chap_challenge_timeout, cstate); + } else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) { + UNTIMEOUT(chap_rechallenge, cstate); + } + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(chap_response_timeout, cstate); + } + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; } /* - * ChapProtocolReject - Peer doesn't grok CHAP. + * chap_protrej - Peer doesn't grok CHAP. */ -static void ChapProtocolReject(int unit) +static void +chap_protrej(int unit) { - chap_state *cstate = &chap[unit]; - - if (cstate->serverstate != CHAPSS_INITIAL && - cstate->serverstate != CHAPSS_CLOSED) - auth_peer_fail(unit, PPP_CHAP); - if (cstate->clientstate != CHAPCS_INITIAL && - cstate->clientstate != CHAPCS_CLOSED) - auth_withpeer_fail(unit, PPP_CHAP); - ChapLowerDown(unit); /* shutdown chap */ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) { + auth_peer_fail(unit, PPP_CHAP); + } + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) { + auth_withpeer_fail(unit, PPP_CHAP); + } + chap_lowerdown(unit); /* shutdown chap */ } /* - * ChapInput - Input CHAP packet. + * chap_input - Input CHAP packet. */ -static void ChapInput(int unit, u_char *inpacket, int packet_len) +static void +chap_input(int unit, u8_t *inpacket, int packet_len) { - chap_state *cstate = &chap[unit]; - u_char *inp; - u_char code, id; - int len; - - /* - * Parse header (code, id and length). - * If packet too short, drop it. - */ - inp = inpacket; - if (packet_len < CHAP_HEADERLEN) { - CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.\n")); - return; - } - GETCHAR(code, inp); - GETCHAR(id, inp); - GETSHORT(len, inp); - if (len < CHAP_HEADERLEN) { - CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.\n")); - return; - } - if (len > packet_len) { - CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.\n")); - return; - } - len -= CHAP_HEADERLEN; - - /* - * Action depends on code (as in fact it usually does :-). - */ - switch (code) { - case CHAP_CHALLENGE: - ChapReceiveChallenge(cstate, inp, id, len); - break; - - case CHAP_RESPONSE: - ChapReceiveResponse(cstate, inp, id, len); - break; - - case CHAP_FAILURE: - ChapReceiveFailure(cstate, inp, id, len); - break; - - case CHAP_SUCCESS: - ChapReceiveSuccess(cstate, inp, id, len); - break; - - default: /* Need code reject? */ - CHAPDEBUG((LOG_WARNING, "Unknown CHAP code (%d) received.\n", code)); - break; - } + chap_state *cstate = &chap[unit]; + u8_t *inp; + u8_t code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "chap_input: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "chap_input: rcvd illegal length.\n")); + return; + } + if (len > packet_len) { + CHAPDEBUG((LOG_INFO, "chap_input: rcvd short packet.\n")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + chap_receive_challenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + chap_receive_response(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + chap_receive_failure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + chap_receive_success(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + CHAPDEBUG((LOG_WARNING, "Unknown CHAP code (%d) received.\n", code)); + break; + } } /* - * ChapReceiveChallenge - Receive Challenge and send Response. + * chap_receive_challenge - Receive Challenge and send Response. */ -static void ChapReceiveChallenge(chap_state *cstate, u_char *inp, int id, int len) +static void +chap_receive_challenge(chap_state *cstate, u8_t *inp, int id, int len) { - int rchallenge_len; - u_char *rchallenge; - int secret_len; - char secret[MAXSECRETLEN]; - char rhostname[256]; - MD5_CTX mdContext; - u_char hash[MD5_SIGNATURE_SIZE]; - - CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.\n", id)); - if (cstate->clientstate == CHAPCS_CLOSED || - cstate->clientstate == CHAPCS_PENDING) { - CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d\n", - cstate->clientstate)); - return; - } - - if (len < 2) { - CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.\n")); - return; - } - - GETCHAR(rchallenge_len, inp); - len -= sizeof (u_char) + rchallenge_len; /* now name field length */ - if (len < 0) { - CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.\n")); - return; - } - rchallenge = inp; - INCPTR(rchallenge_len, inp); - - if (len >= sizeof(rhostname)) - len = sizeof(rhostname) - 1; - BCOPY(inp, rhostname, len); - rhostname[len] = '\000'; - - CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'\n", - rhostname)); - - /* Microsoft doesn't send their name back in the PPP packet */ - if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) { - strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname)); - rhostname[sizeof(rhostname) - 1] = 0; - CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name\n", - rhostname)); - } - - /* get secret for authenticating ourselves with the specified host */ - if (!get_secret(cstate->unit, cstate->resp_name, rhostname, - secret, &secret_len, 0)) { - secret_len = 0; /* assume null secret if can't find one */ - CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating us to %s\n", rhostname)); - } - - /* cancel response send timeout if necessary */ - if (cstate->clientstate == CHAPCS_RESPONSE) - UNTIMEOUT(ChapResponseTimeout, cstate); - - cstate->resp_id = id; - cstate->resp_transmits = 0; - - /* generate MD based on negotiated type */ - switch (cstate->resp_type) { - - case CHAP_DIGEST_MD5: - MD5Init(&mdContext); - MD5Update(&mdContext, &cstate->resp_id, 1); - MD5Update(&mdContext, (u_char*)secret, secret_len); - MD5Update(&mdContext, rchallenge, rchallenge_len); - MD5Final(hash, &mdContext); - BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); - cstate->resp_length = MD5_SIGNATURE_SIZE; - break; - + int rchallenge_len; + u8_t *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + md5_ctx mdContext; + u8_t hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "chap_receive_challenge: Rcvd id %d.\n", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG((LOG_INFO, "chap_receive_challenge: in state %d\n", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "chap_receive_challenge: rcvd short packet.\n")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u8_t) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG((LOG_INFO, "chap_receive_challenge: rcvd short packet.\n")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "chap_receive_challenge: received name field '%s'\n", rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG((LOG_INFO, "chap_receive_challenge: using '%s' as remote name\n", rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating us to %s\n", rhostname)); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(chap_response_timeout, cstate); + } + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + md5_init(&mdContext); + md5_update(&mdContext, &cstate->resp_id, 1); + md5_update(&mdContext, (u8_t*)secret, secret_len); + md5_update(&mdContext, rchallenge, rchallenge_len); + md5_final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + #ifdef CHAPMS - case CHAP_MICROSOFT: - ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); - break; + case CHAP_MICROSOFT: + chapms(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; #endif - - default: - CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->resp_type)); - return; - } - - BZERO(secret, sizeof(secret)); - ChapSendResponse(cstate); + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + chap_send_response(cstate); } /* - * ChapReceiveResponse - Receive and process response. + * chap_receive_response - Receive and process response. */ -static void ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len) +static void +chap_receive_response(chap_state *cstate, u8_t *inp, int id, int len) { - u_char *remmd, remmd_len; - int secret_len, old_state; - int code; - char rhostname[256]; - MD5_CTX mdContext; - char secret[MAXSECRETLEN]; - u_char hash[MD5_SIGNATURE_SIZE]; - - CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.\n", id)); - - if (cstate->serverstate == CHAPSS_CLOSED || - cstate->serverstate == CHAPSS_PENDING) { - CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d\n", - cstate->serverstate)); - return; - } - - if (id != cstate->chal_id) - return; /* doesn't match ID of last challenge */ - - /* - * If we have received a duplicate or bogus Response, - * we have to send the same answer (Success/Failure) - * as we did for the first Response we saw. - */ - if (cstate->serverstate == CHAPSS_OPEN) { - ChapSendStatus(cstate, CHAP_SUCCESS); - return; - } - if (cstate->serverstate == CHAPSS_BADAUTH) { - ChapSendStatus(cstate, CHAP_FAILURE); - return; - } - - if (len < 2) { - CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.\n")); - return; - } - GETCHAR(remmd_len, inp); /* get length of MD */ - remmd = inp; /* get pointer to MD */ - INCPTR(remmd_len, inp); - - len -= sizeof (u_char) + remmd_len; - if (len < 0) { - CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.\n")); - return; - } - - UNTIMEOUT(ChapChallengeTimeout, cstate); - - if (len >= sizeof(rhostname)) - len = sizeof(rhostname) - 1; - BCOPY(inp, rhostname, len); - rhostname[len] = '\000'; - - CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s\n", - rhostname)); - - /* - * Get secret for authenticating them with us, - * do the hash ourselves, and compare the result. - */ - code = CHAP_FAILURE; - if (!get_secret(cstate->unit, rhostname, cstate->chal_name, - secret, &secret_len, 1)) { -/* CHAPDEBUG((LOG_WARNING, TL_CHAP, "No CHAP secret found for authenticating %s\n", rhostname)); */ - CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating %s\n", - rhostname)); - } else { - - /* generate MD based on negotiated type */ - switch (cstate->chal_type) { - - case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ - if (remmd_len != MD5_SIGNATURE_SIZE) - break; /* it's not even the right length */ - MD5Init(&mdContext); - MD5Update(&mdContext, &cstate->chal_id, 1); - MD5Update(&mdContext, (u_char*)secret, secret_len); - MD5Update(&mdContext, cstate->challenge, cstate->chal_len); - MD5Final(hash, &mdContext); - - /* compare local and remote MDs and send the appropriate status */ - if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) - code = CHAP_SUCCESS; /* they are the same! */ - break; - - default: - CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->chal_type)); - } - } - - BZERO(secret, sizeof(secret)); - ChapSendStatus(cstate, code); - - if (code == CHAP_SUCCESS) { - old_state = cstate->serverstate; - cstate->serverstate = CHAPSS_OPEN; - if (old_state == CHAPSS_INITIAL_CHAL) { - auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); - } - if (cstate->chal_interval != 0) - TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); - } else { - CHAPDEBUG((LOG_ERR, "CHAP peer authentication failed\n")); - cstate->serverstate = CHAPSS_BADAUTH; - auth_peer_fail(cstate->unit, PPP_CHAP); - } + u8_t *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + md5_ctx mdContext; + char secret[MAXSECRETLEN]; + u8_t hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "chap_receive_response: Rcvd id %d.\n", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG((LOG_INFO, "chap_receive_response: in state %d\n", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) { + return; /* doesn't match ID of last challenge */ + } + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + chap_send_status(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + chap_send_status(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "chap_receive_response: rcvd short packet.\n")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u8_t) + remmd_len; + if (len < 0) { + CHAPDEBUG((LOG_INFO, "chap_receive_response: rcvd short packet.\n")); + return; + } + + UNTIMEOUT(chap_challenge_timeout, cstate); + + if (len >= sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "chap_receive_response: received name field: %s\n", rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, secret, &secret_len, 1)) { + /* CHAPDEBUG((LOG_WARNING, TL_CHAP, "No CHAP secret found for authenticating %s\n", rhostname)); */ + CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating %s\n", + rhostname)); + } else { + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) { + break; /* it's not even the right length */ + } + md5_init(&mdContext); + md5_update(&mdContext, &cstate->chal_id, 1); + md5_update(&mdContext, (u8_t*)secret, secret_len); + md5_update(&mdContext, cstate->challenge, cstate->chal_len); + md5_final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) { + code = CHAP_SUCCESS; /* they are the same! */ + } + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + chap_send_status(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) { + TIMEOUT(chap_rechallenge, cstate, cstate->chal_interval); + } + } else { + CHAPDEBUG((LOG_ERR, "CHAP peer authentication failed\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } } /* - * ChapReceiveSuccess - Receive Success + * chap_receive_success - Receive Success */ -static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len) +static void +chap_receive_success(chap_state *cstate, u8_t *inp, u8_t id, int len) { + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG((LOG_INFO, "chap_receive_success: Rcvd id %d.\n", id)); + + if (cstate->clientstate == CHAPCS_OPEN) { + /* presumably an answer to a duplicate response */ + return; + } - CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.\n", id)); - - if (cstate->clientstate == CHAPCS_OPEN) - /* presumably an answer to a duplicate response */ - return; - - if (cstate->clientstate != CHAPCS_RESPONSE) { - /* don't know what this is */ - CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n", - cstate->clientstate)); - return; - } - - UNTIMEOUT(ChapResponseTimeout, cstate); - - /* - * Print message. - */ - if (len > 0) - PRINTMSG(inp, len); - - cstate->clientstate = CHAPCS_OPEN; - - auth_withpeer_success(cstate->unit, PPP_CHAP); + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "chap_receive_success: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(chap_response_timeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); } /* - * ChapReceiveFailure - Receive failure. + * chap_receive_failure - Receive failure. */ -static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len) +static void +chap_receive_failure(chap_state *cstate, u8_t *inp, u8_t id, int len) { - CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.\n", id)); - - if (cstate->clientstate != CHAPCS_RESPONSE) { - /* don't know what this is */ - CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n", - cstate->clientstate)); - return; - } - - UNTIMEOUT(ChapResponseTimeout, cstate); - - /* - * Print message. - */ - if (len > 0) - PRINTMSG(inp, len); - - CHAPDEBUG((LOG_ERR, "CHAP authentication failed\n")); - auth_withpeer_fail(cstate->unit, PPP_CHAP); + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG((LOG_INFO, "chap_receive_failure: Rcvd id %d.\n", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "chap_receive_failure: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(chap_response_timeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + CHAPDEBUG((LOG_ERR, "CHAP authentication failed\n")); + auth_withpeer_fail(cstate->unit, PPP_CHAP); } /* - * ChapSendChallenge - Send an Authenticate challenge. + * chap_send_challenge - Send an Authenticate challenge. */ -static void ChapSendChallenge(chap_state *cstate) +static void +chap_send_challenge(chap_state *cstate) { - u_char *outp; - int chal_len, name_len; - int outlen; - - chal_len = cstate->chal_len; - name_len = strlen(cstate->chal_name); - outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; - outp = outpacket_buf[cstate->unit]; - - MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ - - PUTCHAR(CHAP_CHALLENGE, outp); - PUTCHAR(cstate->chal_id, outp); - PUTSHORT(outlen, outp); - - PUTCHAR(chal_len, outp); /* put length of challenge */ - BCOPY(cstate->challenge, outp, chal_len); - INCPTR(chal_len, outp); - - BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ - - pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); - - CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.\n", cstate->chal_id)); - - TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); - ++cstate->chal_transmits; + u8_t *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u8_t) + chal_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + ppp_write(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "chap_send_challenge: Sent id %d.\n", cstate->chal_id)); + + TIMEOUT(chap_challenge_timeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; } /* - * ChapSendStatus - Send a status response (ack or nak). + * chap_send_status - Send a status response (ack or nak). */ -static void ChapSendStatus(chap_state *cstate, int code) +static void +chap_send_status(chap_state *cstate, int code) { - u_char *outp; - int outlen, msglen; - char msg[256]; - - if (code == CHAP_SUCCESS) - strcpy(msg, "Welcome!"); - else - strcpy(msg, "I don't like you. Go 'way."); - msglen = strlen(msg); - - outlen = CHAP_HEADERLEN + msglen; - outp = outpacket_buf[cstate->unit]; - - MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ - - PUTCHAR(code, outp); - PUTCHAR(cstate->chal_id, outp); - PUTSHORT(outlen, outp); - BCOPY(msg, outp, msglen); - pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); - - CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.\n", code, - cstate->chal_id)); + u8_t *outp; + int outlen, msglen; + char msg[256]; + + if (code == CHAP_SUCCESS) { + strcpy(msg, "Welcome!"); + } else { + strcpy(msg, "I don't like you. Go 'way."); + } + msglen = strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + ppp_write(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "chap_send_status: Sent code %d, id %d.\n", code, cstate->chal_id)); } /* - * ChapGenChallenge is used to generate a pseudo-random challenge string of + * chap_gen_challenge is used to generate a pseudo-random challenge string of * a pseudo-random length between min_len and max_len. The challenge * string and its length are stored in *cstate, and various other fields of * *cstate are initialized. */ -static void ChapGenChallenge(chap_state *cstate) +static void +chap_gen_challenge(chap_state *cstate) { - int chal_len; - u_char *ptr = cstate->challenge; - int i; - - /* pick a random challenge length between MIN_CHALLENGE_LENGTH and - MAX_CHALLENGE_LENGTH */ - chal_len = (unsigned) - ((((magic() >> 16) * - (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16) - + MIN_CHALLENGE_LENGTH); - cstate->chal_len = chal_len; - cstate->chal_id = ++cstate->id; - cstate->chal_transmits = 0; - - /* generate a random string */ - for (i = 0; i < chal_len; i++ ) - *ptr++ = (char) (magic() & 0xff); -} + int chal_len; + u8_t *ptr = cstate->challenge; + int i; -/* - * ChapSendResponse - send a response packet with values as specified - * in *cstate. - */ -/* ARGSUSED */ -static void ChapSendResponse(chap_state *cstate) -{ - u_char *outp; - int outlen, md_len, name_len; - - md_len = cstate->resp_length; - name_len = strlen(cstate->resp_name); - outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; - outp = outpacket_buf[cstate->unit]; - - MAKEHEADER(outp, PPP_CHAP); - - PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ - PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ - PUTSHORT(outlen, outp); /* packet length */ - - PUTCHAR(md_len, outp); /* length of MD */ - BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ - INCPTR(md_len, outp); - - BCOPY(cstate->resp_name, outp, name_len); /* append our name */ - - /* send the packet */ - pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); - - cstate->clientstate = CHAPCS_RESPONSE; - TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); - ++cstate->resp_transmits; + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) + ((((magic_next() >> 16) * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16) + + MIN_CHALLENGE_LENGTH); + cstate->chal_len = chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) { + *ptr++ = (char) (magic_next() & 0xff); + } } /* - * ChapPrintPkt - print the contents of a CHAP packet. + * chap_send_response - send a response packet with values as specified + * in *cstate. */ -static int ChapPrintPkt( - u_char *p, - int plen, - void (*printer) (void *, char *, ...), - void *arg -) +/* ARGSUSED */ +static void +chap_send_response(chap_state *cstate) { - int code, id, len; - int clen, nlen; - u_char x; - - if (plen < CHAP_HEADERLEN) - return 0; - GETCHAR(code, p); - GETCHAR(id, p); - GETSHORT(len, p); - if (len < CHAP_HEADERLEN || len > plen) - return 0; - - if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) - printer(arg, " %s", ChapCodenames[code-1]); - else - printer(arg, " code=0x%x", code); - printer(arg, " id=0x%x", id); - len -= CHAP_HEADERLEN; - switch (code) { - case CHAP_CHALLENGE: - case CHAP_RESPONSE: - if (len < 1) - break; - clen = p[0]; - if (len < clen + 1) - break; - ++p; - nlen = len - clen - 1; - printer(arg, " <"); - for (; clen > 0; --clen) { - GETCHAR(x, p); - printer(arg, "%.2x", x); - } - printer(arg, ">, name = %.*Z", nlen, p); - break; - case CHAP_FAILURE: - case CHAP_SUCCESS: - printer(arg, " %.*Z", len, p); - break; - default: - for (clen = len; clen > 0; --clen) { - GETCHAR(x, p); - printer(arg, " %.2x", x); - } - } - - return len + CHAP_HEADERLEN; + u8_t *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u8_t) + md_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + ppp_write(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(chap_response_timeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; } +#if 0 +static char *chap_codenames[] = { + "Challenge", "Response", "Success", "Failure" +}; +/* + * chap_printpkt - print the contents of a CHAP packet. + */ +static int +chap_printpkt( u8_t *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len; + int clen, nlen; + u8_t x; + + if (plen < CHAP_HEADERLEN) { + return 0; + } + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) { + return 0; + } + if (code >= 1 && code <= sizeof(chap_codenames) / sizeof(char *)) { + printer(arg, " %s", chap_codenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) { + break; + } + clen = p[0]; + if (len < clen + 1) { + break; + } + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = %.*Z", nlen, p); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " %.*Z", len, p); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} #endif +#endif /* CHAP_SUPPORT */ + #endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/chap.h b/packages/net/lwip_tcpip/current/src/netif/ppp/chap.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/chap.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/chap.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-03 Guy Lancaster , Global Election Systems Inc. -* Original built from BSD network code. +* Original built from BSD network code. ******************************************************************************/ /* * chap.h - Challenge Handshake Authentication Protocol definitions. @@ -62,7 +62,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: chap.h,v 1.2 2006/03/29 10:33:28 asl Exp $ + * $Id: chap.h,v 1.3 2010/01/26 11:27:49 jld Exp $ */ #ifndef CHAP_H @@ -73,49 +73,49 @@ *************************/ /* Code + ID + length */ -#define CHAP_HEADERLEN 4 +#define CHAP_HEADERLEN 4 /* * CHAP codes. */ -#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ -#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ -#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ -#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ -#define CHAP_CHALLENGE 1 -#define CHAP_RESPONSE 2 -#define CHAP_SUCCESS 3 -#define CHAP_FAILURE 4 +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 /* * Challenge lengths (for challenges we send) and other limits. */ -#define MIN_CHALLENGE_LENGTH 32 -#define MAX_CHALLENGE_LENGTH 64 -#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ /* * Client (peer) states. */ -#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ -#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ -#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ -#define CHAPCS_LISTEN 3 /* Listening for a challenge */ -#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ -#define CHAPCS_OPEN 5 /* We've received Success */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ /* * Server (authenticator) states. */ -#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ -#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ -#define CHAPSS_PENDING 2 /* Auth peer when lower up */ -#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ -#define CHAPSS_OPEN 4 /* We've sent a Success msg */ -#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ -#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ /************************ *** PUBLIC DATA TYPES *** @@ -126,25 +126,25 @@ */ typedef struct chap_state { - int unit; /* Interface unit number */ - int clientstate; /* Client state */ - int serverstate; /* Server state */ - u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ - u_char chal_len; /* challenge length */ - u_char chal_id; /* ID of last challenge */ - u_char chal_type; /* hash algorithm for challenges */ - u_char id; /* Current id */ - char *chal_name; /* Our name to use with challenge */ - int chal_interval; /* Time until we challenge peer again */ - int timeouttime; /* Timeout time in seconds */ - int max_transmits; /* Maximum # of challenge transmissions */ - int chal_transmits; /* Number of transmissions of challenge */ - int resp_transmits; /* Number of transmissions of response */ - u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ - u_char resp_length; /* length of response */ - u_char resp_id; /* ID for response messages */ - u_char resp_type; /* hash algorithm for responses */ - char *resp_name; /* Our name to send with response */ + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u8_t challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u8_t chal_len; /* challenge length */ + u8_t chal_id; /* ID of last challenge */ + u8_t chal_type; /* hash algorithm for challenges */ + u8_t id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u8_t response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u8_t resp_length; /* length of response */ + u8_t resp_id; /* ID for response messages */ + u8_t resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ } chap_state; @@ -160,8 +160,7 @@ extern struct protent chap_protent; *** PUBLIC FUNCTIONS *** ***********************/ -void ChapAuthWithPeer (int, char *, int); -void ChapAuthPeer (int, char *, int); +void chap_auth_with_peer(int, char *, int); +void chap_auth_peer(int, char *, int); #endif /* CHAP_H */ - diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/chat.c b/packages/net/lwip_tcpip/current/src/netif/ppp/chat.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/chat.c @@ -0,0 +1,332 @@ +/** + * @file + * Chat implementation + * + */ + +/* + * Copyright (c) 2001-2004 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. + * + * Author: Simon Kallweit + */ + +/* + * This is an arch independent chat implementation. The specific serial hooks + * must be provided by another file. They are sio_open, sio_recv and sio_send. + */ + +#include "netif/ppp/chat.h" +#include "lwip/opt.h" + +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/sio.h" + +#define BUFSIZE 64 + +/** Chat states */ +enum chat_state { + STATE_INITIAL, /**< Initial state, checks the current item, executes it + directly or transitions to a new state to handle the + item. */ + STATE_WAIT, /**< Waits for an answer from the modem. */ + STATE_SLEEP, /**< Sleep for at least 1 second. */ + STATE_NEXT_ITEM, /**< Moves to the next item. */ + STATE_IDLE, /**< Chat finished. */ +}; + +/** Chat */ +struct chat { + struct chat *next; /**< Pointer to the next chat instance */ + /* Settings */ + sio_fd_t sd; /**< Serial device descriptor */ + const struct chat_item const *items; /**< Chat items */ + int timeout; /**< Default timeout in seconds */ + chat_cb_t chat_cb; /**< Chat callback function */ + chat_send_cb_t send_cb; /**< Chat send callback function */ + void *arg; /**< Argument for callback function */ + /* State */ + enum chat_state state; /**< Current state */ + const struct chat_item *item; /**< Current chat item */ + char buf[BUFSIZE + 1]; /**< Receive buffer */ + int recvd; /**< Received bytes */ + int timeleft; /**< Seconds left until timeout */ +}; + +/** Linked list of chats */ +static struct chat *chats; + +static void chat_send(struct chat *chat, const struct chat_item *item); +static void chat_send_cb(struct chat *chat, const struct chat_item *item); +static int chat_wait(struct chat *chat, const struct chat_item *item); + +/* + * Creates a new chat. The chat is further processed by calling chat_poll(). + * The result of the chat is posted by the chat_cb() callback function. + */ +chat_t chat_new(sio_fd_t sd, const struct chat_item const *items, int timeout, + chat_cb_t chat_cb, chat_send_cb_t send_cb, void *arg) +{ + struct chat *chat; + const struct chat_item *item; + + /* Some sanity checks */ + LWIP_ASSERT("chat_new: sd != NULL", sd != NULL); + LWIP_ASSERT("chat_new: items != NULL", items != NULL); + LWIP_ASSERT("chat_new: chat_cb != NULL", chat_cb != NULL); + + /* Check for correct chat items */ + for (item = items; item->cmd != CHAT_LAST; item++) + LWIP_ASSERT("chat_new: invalid chat item or CHAT_LAST missing", + item->cmd >= CHAT_ABORT && item->cmd < CHAT_LAST); + + /* Allocate chat */ + chat = mem_malloc(sizeof(struct chat)); + if (!chat) + return NULL; + + /* Iinitialize chat */ + chat->sd = sd; + chat->items = items; + chat->timeout = timeout * 1000 + 1; + chat->chat_cb = chat_cb; + chat->send_cb = send_cb; + chat->arg = arg; + + /* Reset state */ + chat->state = STATE_INITIAL; + chat->item = items; + chat->recvd = 0; + + /* Add chat to list */ + chat->next = chats; + chats = chat; + + return chat; +} + +/* + * Frees a chat. + */ +void chat_free(struct chat *chat) +{ + struct chat *t; + + if (!chat) + return; + + /* Remove chat from list */ + if (chat == chats) { + chats = chat->next; + } else { + for (t = chats; t->next; t = t->next) + if (t->next == chat) { + t->next = chat->next; + break; + } + } + + mem_free(chat); +} + +/* + * Processes the chat. This needs to be called iteratively until the chat_cb() + * callback function posts success or an error. + */ +void chat_poll(struct chat *chat) +{ + int done = 0; + int err; + + LWIP_ASSERT("chat_poll: chat != NULL", chat != NULL); + + while (!done) { + switch (chat->state) { + case STATE_INITIAL: + /* Check item command */ + switch (chat->item->cmd) { + case CHAT_ABORT: + chat->state = STATE_NEXT_ITEM; + break; + case CHAT_SAY: + LWIP_DEBUGF(PPP_DEBUG, ("chat_poll: SAY '%s'\n", chat->item->arg)); + chat->state = STATE_NEXT_ITEM; + break; + case CHAT_SEND: + LWIP_DEBUGF(PPP_DEBUG, ("chat_poll: SEND '%s'\n", chat->item->arg)); + chat_send(chat, chat->item); + chat->state = STATE_NEXT_ITEM; + break; + case CHAT_SEND_CB: + LWIP_DEBUGF(PPP_DEBUG, ("chat_poll: SEND_CB '%d'\n", (int) chat->item->arg)); + chat_send_cb(chat, chat->item); + chat->state = STATE_NEXT_ITEM; + break; + case CHAT_WAIT: + LWIP_DEBUGF(PPP_DEBUG, ("chat_poll: WAIT '%s'\n", chat->item->arg)); + chat->timeleft = chat->timeout; + chat->state = STATE_WAIT; + break; + case CHAT_SLEEP: + LWIP_DEBUGF(PPP_DEBUG, ("chat_poll: SLEEP\n")); + chat->timeleft = 1000 + 1; + if (chat->item->arg) + chat->timeleft = (int) chat->item->arg + 1; + chat->state = STATE_SLEEP; + break; + case CHAT_LAST: + LWIP_DEBUGF(PPP_DEBUG, ("chat_poll: SUCCESS\n")); + chat->chat_cb(chat, CHAT_ERR_OK, chat->arg); + chat->state = STATE_IDLE; + break; + } + break; + case STATE_WAIT: + err = chat_wait(chat, chat->item); + if (err == -1) { + done = 1; + } else if (err == CHAT_ERR_OK) { + chat->state = STATE_NEXT_ITEM; + } else { + chat->chat_cb(chat, err, chat->arg); + chat->state = STATE_IDLE; + } + break; + case STATE_SLEEP: + if (chat->timeleft == 0) + chat->state = STATE_NEXT_ITEM; + else + done = 1; + break; + case STATE_NEXT_ITEM: + chat->item++; + chat->state = STATE_INITIAL; + break; + case STATE_IDLE: + done = 1; + break; + } + } +} + +/* + * This should be called every CHAT_TMR_INTERVAL milliseconds. + */ +void chat_tmr(void) +{ + struct chat *chat; + + LWIP_DEBUGF(PPP_DEBUG, ("chat_tmr\n")); + + for (chat = chats; chat; chat = chat->next) { + if (chat->timeleft > CHAT_TMR_INTERVAL) + chat->timeleft -= CHAT_TMR_INTERVAL; + else + chat->timeleft = 0; + LWIP_DEBUGF(PPP_DEBUG, ("chat_tmr: %d\n", chat->timeleft)); + } +} + + +/** + * Sends a chat message to the modem. + * @param chat Chat + * @param item Item to send + */ +static void chat_send(struct chat *chat, const struct chat_item *item) +{ + sio_write(chat->sd, (u8_t *) item->arg, strlen(item->arg)); + chat->recvd = 0; +} + +/** + * Sends a chat message to the modem. Gets the send string via a callback. + * @param chat Chat + * @param item Item to send + */ +static void chat_send_cb(struct chat *chat, const struct chat_item *item) +{ + LWIP_ASSERT("chat_poll: chat->send_cb != NULL", chat->send_cb != NULL); + + // Get the command to send + chat->send_cb(chat, (int) item->arg, chat->buf, BUFSIZE, chat->arg); + chat->buf[BUFSIZE] = '\0'; + + // Send command + sio_write(chat->sd, (u8_t *) chat->buf, strlen(chat->buf)); + chat->recvd = 0; +} + +/** + * Waits for an response from the modem. This function returns: + * -1: when no more data could be read from the serial (polling) + * CHAT_ERR_OK: when the correct response was received + * CHAT_ERR_ABORT: when an abort response was received + * CHAT_ERR_TIMEOUT: when a timeout occured + * @param chat Chat + * @param item Item to wait for + * @return See above. + */ +static int chat_wait(struct chat *chat, const struct chat_item *item) +{ + int count; + const struct chat_item *abort; + + while (chat->recvd < BUFSIZE) { + /* Receive data from serial */ + if ((count = sio_read(chat->sd, (u8_t *) &chat->buf[chat->recvd], + BUFSIZE - chat->recvd)) < 1) + break; + chat->recvd += count; + chat->buf[chat->recvd] = '\0'; + + /* Check abort strings */ + for (abort = chat->items; abort->cmd != CHAT_LAST; abort++) { + if (abort->cmd != CHAT_ABORT) + continue; + if (strstr(chat->buf, abort->arg)) { + LWIP_DEBUGF(PPP_DEBUG, ("chat_wait: ABORT '%s'\n", abort->arg)); + return CHAT_ERR_ABORT; + } + } + + /* Check wait string */ + if (strstr(chat->buf, item->arg)) { + LWIP_DEBUGF(PPP_DEBUG, ("chat_wait: SUCCESS\n")); + return CHAT_ERR_OK; + } + } + + /* Check for timeout */ + if (chat->timeleft == 0) { + LWIP_DEBUGF(PPP_DEBUG, ("chat_wait: TIMEOUT\n")); + return CHAT_ERR_TIMEOUT; + } + + return -1; +} diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.c b/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.c @@ -8,13 +8,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -29,7 +29,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-08 Guy Lancaster , Global Election Systems Inc. -* Original based on BSD chap_ms.c. +* Original based on BSD chap_ms.c. *****************************************************************************/ /* * chap_ms.c - Microsoft MS-CHAP compatible implementation. @@ -66,10 +66,14 @@ #define USE_CRYPT +#include "lwip/opt.h" -#include "ppp.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#if MSCHAP_SUPPORT > 0 +#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" #include "md4.h" #ifndef USE_CRYPT @@ -77,7 +81,6 @@ #endif #include "chap.h" #include "chpms.h" -#include "pppdebug.h" /*************************/ @@ -89,9 +92,9 @@ /*** LOCAL DATA TYPES ***/ /************************/ typedef struct { - u_char LANManResp[24]; - u_char NTResp[24]; - u_char UseNT; /* If 1, ignore the LANMan response field */ + u8_t LANManResp[24]; + u8_t NTResp[24]; + u8_t UseNT; /* If 1, ignore the LANMan response field */ } MS_ChapResponse; /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), in case this struct gets padded. */ @@ -106,173 +109,163 @@ typedef struct { extern void setkey(const char *); extern void encrypt(char *, int); -static void DesEncrypt (u_char *, u_char *, u_char *); -static void MakeKey (u_char *, u_char *); +static void DesEncrypt (u8_t *, u8_t *, u8_t *); +static void MakeKey (u8_t *, u8_t *); #ifdef USE_CRYPT -static void Expand (u_char *, u_char *); -static void Collapse (u_char *, u_char *); +static void Expand (u8_t *, u8_t *); +static void Collapse (u8_t *, u8_t *); #endif static void ChallengeResponse( - u_char *challenge, /* IN 8 octets */ - u_char *pwHash, /* IN 16 octets */ - u_char *response /* OUT 24 octets */ + u8_t *challenge, /* IN 8 octets */ + u8_t *pwHash, /* IN 16 octets */ + u8_t *response /* OUT 24 octets */ ); static void ChapMS_NT( - char *rchallenge, - int rchallenge_len, - char *secret, - int secret_len, - MS_ChapResponse *response + char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response ); -static u_char Get7Bits( - u_char *input, - int startBit +static u8_t Get7Bits( + u8_t *input, + int startBit ); /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ /***********************************/ -void ChapMS( - chap_state *cstate, - char *rchallenge, - int rchallenge_len, - char *secret, - int secret_len -) +void +chapms( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len) { - MS_ChapResponse response; + MS_ChapResponse response; #ifdef MSLANMAN - extern int ms_lanman; + extern int ms_lanman; #endif - + #if 0 - CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'\n", secret_len, secret)); + CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'\n", secret_len, secret)); #endif - BZERO(&response, sizeof(response)); - - /* Calculate both always */ - ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); - + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + #ifdef MSLANMAN - ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); - - /* prefered method is set by option */ - response.UseNT = !ms_lanman; + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; #else - response.UseNT = 1; + response.UseNT = 1; #endif - - BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); - cstate->resp_length = MS_CHAP_RESPONSE_LEN; + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; } /**********************************/ /*** LOCAL FUNCTION DEFINITIONS ***/ /**********************************/ -static void ChallengeResponse( - u_char *challenge, /* IN 8 octets */ - u_char *pwHash, /* IN 16 octets */ - u_char *response /* OUT 24 octets */ -) +static void +ChallengeResponse( u8_t *challenge, /* IN 8 octets */ + u8_t *pwHash, /* IN 16 octets */ + u8_t *response /* OUT 24 octets */) { - char ZPasswordHash[21]; - - BZERO(ZPasswordHash, sizeof(ZPasswordHash)); - BCOPY(pwHash, ZPasswordHash, 16); - + char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, 16); + #if 0 - log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); #endif - - DesEncrypt(challenge, ZPasswordHash + 0, response + 0); - DesEncrypt(challenge, ZPasswordHash + 7, response + 8); - DesEncrypt(challenge, ZPasswordHash + 14, response + 16); - + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + #if 0 - log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); #endif } #ifdef USE_CRYPT -static void DesEncrypt( - u_char *clear, /* IN 8 octets */ - u_char *key, /* IN 7 octets */ - u_char *cipher /* OUT 8 octets */ -) +static void +DesEncrypt( u8_t *clear, /* IN 8 octets */ + u8_t *key, /* IN 7 octets */ + u8_t *cipher /* OUT 8 octets */) { - u_char des_key[8]; - u_char crypt_key[66]; - u_char des_input[66]; - - MakeKey(key, des_key); - - Expand(des_key, crypt_key); - setkey(crypt_key); - + u8_t des_key[8]; + u8_t crypt_key[66]; + u8_t des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey(crypt_key); + #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", - clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); #endif - - Expand(clear, des_input); - encrypt(des_input, 0); - Collapse(des_input, cipher); - + + Expand(clear, des_input); + encrypt(des_input, 0); + Collapse(des_input, cipher); + #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", - cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); #endif } #else /* USE_CRYPT */ -static void DesEncrypt( - u_char *clear, /* IN 8 octets */ - u_char *key, /* IN 7 octets */ - u_char *cipher /* OUT 8 octets */ -) +static void +DesEncrypt( u8_t *clear, /* IN 8 octets */ + u8_t *key, /* IN 7 octets */ + u8_t *cipher /* OUT 8 octets */) { - des_cblock des_key; - des_key_schedule key_schedule; - - MakeKey(key, des_key); - - des_set_key(&des_key, key_schedule); - + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", - clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); #endif - - des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); - + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", - cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); #endif } #endif /* USE_CRYPT */ -static u_char Get7Bits( - u_char *input, - int startBit -) +static u8_t +Get7Bits( u8_t *input, int startBit) { - register unsigned int word; - - word = (unsigned)input[startBit / 8] << 8; - word |= (unsigned)input[startBit / 8 + 1]; - - word >>= 15 - (startBit % 8 + 7); - - return word & 0xFE; + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; } #ifdef USE_CRYPT @@ -281,118 +274,123 @@ static u_char Get7Bits( * out == 64-byte string where each byte is either 1 or 0 * Note that the low-order "bit" is always ignored by by setkey() */ -static void Expand(u_char *in, u_char *out) +static void +Expand(u8_t *in, u8_t *out) { - int j, c; - int i; - - for(i = 0; i < 64; in++){ - c = *in; - for(j = 7; j >= 0; j--) - *out++ = (c >> j) & 01; - i += 8; - } + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) { + *out++ = (c >> j) & 01; + } + i += 8; + } } /* The inverse of Expand */ -static void Collapse(u_char *in, u_char *out) +static void +Collapse(u8_t *in, u8_t *out) { - int j; - int i; - unsigned int c; - - for (i = 0; i < 64; i += 8, out++) { - c = 0; - for (j = 7; j >= 0; j--, in++) - c |= *in << j; - *out = c & 0xff; - } + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) { + c |= *in << j; + } + *out = c & 0xff; + } } #endif -static void MakeKey( - u_char *key, /* IN 56 bit DES key missing parity bits */ - u_char *des_key /* OUT 64 bit DES key with parity bits added */ -) +static void +MakeKey( u8_t *key, /* IN 56 bit DES key missing parity bits */ + u8_t *des_key /* OUT 64 bit DES key with parity bits added */) { - des_key[0] = Get7Bits(key, 0); - des_key[1] = Get7Bits(key, 7); - des_key[2] = Get7Bits(key, 14); - des_key[3] = Get7Bits(key, 21); - des_key[4] = Get7Bits(key, 28); - des_key[5] = Get7Bits(key, 35); - des_key[6] = Get7Bits(key, 42); - des_key[7] = Get7Bits(key, 49); - + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + #ifndef USE_CRYPT - des_set_odd_parity((des_cblock *)des_key); + des_set_odd_parity((des_cblock *)des_key); #endif - + #if 0 - CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n", - key[0], key[1], key[2], key[3], key[4], key[5], key[6])); - CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n", - des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); + CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); #endif } -static void ChapMS_NT( - char *rchallenge, - int rchallenge_len, - char *secret, - int secret_len, - MS_ChapResponse *response -) +static void +ChapMS_NT( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) { - int i; - MDstruct md4Context; - u_char unicodePassword[MAX_NT_PASSWORD * 2]; - static int low_byte_first = -1; - - /* Initialize the Unicode version of the secret (== password). */ - /* This implicitly supports 8-bit ISO8859/1 characters. */ - BZERO(unicodePassword, sizeof(unicodePassword)); - for (i = 0; i < secret_len; i++) - unicodePassword[i * 2] = (u_char)secret[i]; - - MDbegin(&md4Context); - MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ - - if (low_byte_first == -1) - low_byte_first = (htons((unsigned short int)1) != 1); - if (low_byte_first == 0) - MDreverse((u_long *)&md4Context); /* sfb 961105 */ - - MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */ - - ChallengeResponse(rchallenge, (char *)md4Context.buffer, response->NTResp); + int i; + MDstruct md4Context; + u8_t unicodePassword[MAX_NT_PASSWORD * 2]; + static int low_byte_first = -1; + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) { + unicodePassword[i * 2] = (u8_t)secret[i]; + } + MDbegin(&md4Context); + MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + + if (low_byte_first == -1) { + low_byte_first = (htons((unsigned short int)1) != 1); + } + if (low_byte_first == 0) { + MDreverse((u32_t *)&md4Context); /* sfb 961105 */ + } + + MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */ + + ChallengeResponse(rchallenge, (char *)md4Context.buffer, response->NTResp); } #ifdef MSLANMAN -static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ +static u8_t *StdText = (u8_t *)"KGS!@#$%"; /* key from rasapi32.dll */ -static ChapMS_LANMan( - char *rchallenge, - int rchallenge_len, - char *secret, - int secret_len, - MS_ChapResponse *response -) +static void +ChapMS_LANMan( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) { - int i; - u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ - u_char PasswordHash[16]; - - /* LANMan password is case insensitive */ - BZERO(UcasePassword, sizeof(UcasePassword)); - for (i = 0; i < secret_len; i++) - UcasePassword[i] = (u_char)toupper(secret[i]); - DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); - DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); - ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); + int i; + u8_t UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u8_t PasswordHash[16]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) { + UcasePassword[i] = (u8_t)toupper(secret[i]); + } + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); } #endif #endif /* MSCHAP_SUPPORT */ +#endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.h b/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/chpms.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 98-01-30 Guy Lancaster , Global Election Systems Inc. -* Original built from BSD network code. +* Original built from BSD network code. ******************************************************************************/ /* * chap.h - Challenge Handshake Authentication Protocol definitions. @@ -51,14 +51,14 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: chpms.h,v 1.2 2006/03/29 10:33:28 asl Exp $ + * $Id: chpms.h,v 1.3 2010/01/26 11:27:49 jld Exp $ */ #ifndef CHPMS_H #define CHPMS_H -#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ -void ChapMS (chap_state *, char *, int, char *, int); +void chapms(chap_state *, char *, int, char *, int); #endif /* CHPMS_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.c b/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-01 Guy Lancaster , Global Election Systems Inc. -* Original based on BSD fsm.c. +* Original based on BSD fsm.c. *****************************************************************************/ /* * fsm.c - {Link, IP} Control Protocol Finite State Machine. @@ -49,23 +49,43 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ - /* * TODO: * Randomize fsm id on link/init. * Deal with variable outgoing MTU. */ -#include "ppp.h" -#if PPP_SUPPORT > 0 +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" +#include "timesys.h" + #include "fsm.h" -#include "pppdebug.h" /*************************/ /*** LOCAL DEFINITIONS ***/ /*************************/ +#if PPP_DEBUG + +static const char *ppperr_strerr[] = { + "LS_INITIAL", /* LS_INITIAL 0 */ + "LS_STARTING", /* LS_STARTING 1 */ + "LS_CLOSED", /* LS_CLOSED 2 */ + "LS_STOPPED", /* LS_STOPPED 3 */ + "LS_CLOSING", /* LS_CLOSING 4 */ + "LS_STOPPING", /* LS_STOPPING 5 */ + "LS_REQSENT", /* LS_REQSENT 6 */ + "LS_ACKRCVD", /* LS_ACKRCVD 7 */ + "LS_ACKSENT", /* LS_ACKSENT 8 */ + "LS_OPENED" /* LS_OPENED 9 */ +}; + +#endif /* PPP_DEBUG */ /************************/ /*** LOCAL DATA TYPES ***/ @@ -76,15 +96,15 @@ /*** LOCAL FUNCTION DECLARATIONS ***/ /***********************************/ static void fsm_timeout (void *); -static void fsm_rconfreq (fsm *, u_char, u_char *, int); -static void fsm_rconfack (fsm *, int, u_char *, int); -static void fsm_rconfnakrej (fsm *, int, int, u_char *, int); -static void fsm_rtermreq (fsm *, int, u_char *, int); +static void fsm_rconfreq (fsm *, u8_t, u8_t *, int); +static void fsm_rconfack (fsm *, int, u8_t *, int); +static void fsm_rconfnakrej (fsm *, int, int, u8_t *, int); +static void fsm_rtermreq (fsm *, int, u8_t *, int); static void fsm_rtermack (fsm *); -static void fsm_rcoderej (fsm *, u_char *, int); +static void fsm_rcoderej (fsm *, u8_t *, int); static void fsm_sconfreq (fsm *, int); -#define PROTO_NAME(f) ((f)->callbacks->proto_name) +#define PROTO_NAME(f) ((f)->callbacks->proto_name) /******************************/ @@ -107,48 +127,52 @@ int peer_mru[NUM_PPP]; * * Initialize fsm state. */ -void fsm_init(fsm *f) +void +fsm_init(fsm *f) { - f->state = INITIAL; - f->flags = 0; - f->id = 0; /* XXX Start with random id? */ - f->timeouttime = FSM_DEFTIMEOUT; - f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; - f->maxtermtransmits = FSM_DEFMAXTERMREQS; - f->maxnakloops = FSM_DEFMAXNAKLOOPS; - f->term_reason_len = 0; + f->state = LS_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = FSM_DEFTIMEOUT; + f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; + f->maxtermtransmits = FSM_DEFMAXTERMREQS; + f->maxnakloops = FSM_DEFMAXNAKLOOPS; + f->term_reason_len = 0; } /* * fsm_lowerup - The lower layer is up. */ -void fsm_lowerup(fsm *f) +void +fsm_lowerup(fsm *f) { - int oldState = f->state; + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_CLOSED; + break; - switch( f->state ){ - case INITIAL: - f->state = CLOSED; - break; - - case STARTING: - if( f->flags & OPT_SILENT ) - f->state = STOPPED; - else { - /* Send an initial configure-request */ - fsm_sconfreq(f, 0); - f->state = REQSENT; - } - break; - - default: - FSMDEBUG((LOG_INFO, "%s: Up event in state %d!\n", - PROTO_NAME(f), f->state)); - } - - FSMDEBUG((LOG_INFO, "%s: lowerup state %d -> %d\n", - PROTO_NAME(f), oldState, f->state)); + case LS_STARTING: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Up event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG((LOG_INFO, "%s: lowerup state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); } @@ -157,88 +181,97 @@ void fsm_lowerup(fsm *f) * * Cancel all timeouts and inform upper layers. */ -void fsm_lowerdown(fsm *f) +void +fsm_lowerdown(fsm *f) { - int oldState = f->state; - - switch( f->state ){ - case CLOSED: - f->state = INITIAL; - break; - - case STOPPED: - f->state = STARTING; - if( f->callbacks->starting ) - (*f->callbacks->starting)(f); - break; - - case CLOSING: - f->state = INITIAL; - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - break; - - case STOPPING: - case REQSENT: - case ACKRCVD: - case ACKSENT: - f->state = STARTING; - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - break; - - case OPENED: - if( f->callbacks->down ) - (*f->callbacks->down)(f); - f->state = STARTING; - break; - - default: - FSMDEBUG((LOG_INFO, "%s: Down event in state %d!\n", - PROTO_NAME(f), f->state)); - } - - FSMDEBUG((LOG_INFO, "%s: lowerdown state %d -> %d\n", - PROTO_NAME(f), oldState, f->state)); + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_CLOSED: + f->state = LS_INITIAL; + break; + + case LS_STOPPED: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSING: + f->state = LS_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + f->state = LS_STARTING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Down event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG((LOG_INFO, "%s: lowerdown state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); } /* * fsm_open - Link is allowed to come up. */ -void fsm_open(fsm *f) +void +fsm_open(fsm *f) { - int oldState = f->state; - - switch( f->state ){ - case INITIAL: - f->state = STARTING; - if( f->callbacks->starting ) - (*f->callbacks->starting)(f); - break; - - case CLOSED: - if( f->flags & OPT_SILENT ) - f->state = STOPPED; - else { - /* Send an initial configure-request */ - fsm_sconfreq(f, 0); - f->state = REQSENT; - } - break; - - case CLOSING: - f->state = STOPPING; - /* fall through */ - case STOPPED: - case OPENED: - if( f->flags & OPT_RESTART ){ - fsm_lowerdown(f); - fsm_lowerup(f); - } - break; - } - - FSMDEBUG((LOG_INFO, "%s: open state %d -> %d\n", - PROTO_NAME(f), oldState, f->state)); + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSED: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + case LS_CLOSING: + f->state = LS_STOPPING; + /* fall through */ + case LS_STOPPED: + case LS_OPENED: + if( f->flags & OPT_RESTART ) { + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } + + FSMDEBUG((LOG_INFO, "%s: open state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); } @@ -246,47 +279,50 @@ void fsm_open(fsm *f) * fsm_close - Start closing connection. * * Cancel timeouts and either initiate close or possibly go directly to - * the CLOSED state. + * the LS_CLOSED state. */ -void fsm_close(fsm *f, char *reason) +void +fsm_close(fsm *f, char *reason) { - int oldState = f->state; - - f->term_reason = reason; - f->term_reason_len = (reason == NULL? 0: strlen(reason)); - switch( f->state ){ - case STARTING: - f->state = INITIAL; - break; - case STOPPED: - f->state = CLOSED; - break; - case STOPPING: - f->state = CLOSING; - break; - - case REQSENT: - case ACKRCVD: - case ACKSENT: - case OPENED: - if( f->state != OPENED ) - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - else if( f->callbacks->down ) - (*f->callbacks->down)(f); /* Inform upper layers we're down */ - - /* Init restart counter, send Terminate-Request */ - f->retransmits = f->maxtermtransmits; - fsm_sdata(f, TERMREQ, f->reqid = ++f->id, - (u_char *) f->term_reason, f->term_reason_len); - TIMEOUT(fsm_timeout, f, f->timeouttime); - --f->retransmits; - - f->state = CLOSING; - break; - } - - FSMDEBUG((LOG_INFO, "%s: close reason=%s state %d -> %d\n", - PROTO_NAME(f), reason, oldState, f->state)); + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + f->term_reason = reason; + f->term_reason_len = (reason == NULL? 0: strlen(reason)); + switch( f->state ) { + case LS_STARTING: + f->state = LS_INITIAL; + break; + case LS_STOPPED: + f->state = LS_CLOSED; + break; + case LS_STOPPING: + f->state = LS_CLOSING; + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + case LS_OPENED: + if( f->state != LS_OPENED ) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + } else if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u8_t *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_CLOSING; + break; + } + + FSMDEBUG((LOG_INFO, "%s: close reason=%s state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); } @@ -295,108 +331,107 @@ void fsm_close(fsm *f, char *reason) * * Used for all packets sent to our peer by this module. */ -void fsm_sdata( - fsm *f, - u_char code, - u_char id, - u_char *data, - int datalen -) +void +fsm_sdata( fsm *f, u8_t code, u8_t id, u8_t *data, int datalen) { - u_char *outp; - int outlen; - - /* Adjust length to be smaller than MTU */ - outp = outpacket_buf[f->unit]; - if (datalen > peer_mru[f->unit] - (int)HEADERLEN) - datalen = peer_mru[f->unit] - HEADERLEN; - if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) - BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); - outlen = datalen + HEADERLEN; - MAKEHEADER(outp, f->protocol); - PUTCHAR(code, outp); - PUTCHAR(id, outp); - PUTSHORT(outlen, outp); - pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); - FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d,%d,%d.\n", - PROTO_NAME(f), code, id, outlen)); + u8_t *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf[f->unit]; + if (datalen > peer_mru[f->unit] - (int)HEADERLEN) { + datalen = peer_mru[f->unit] - HEADERLEN; + } + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) { + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + } + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + ppp_write(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); + FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d,%d,%d.\n", + PROTO_NAME(f), code, id, outlen)); } /* * fsm_input - Input packet. */ -void fsm_input(fsm *f, u_char *inpacket, int l) +void +fsm_input(fsm *f, u8_t *inpacket, int l) { - u_char *inp = inpacket; - u_char code, id; - int len; - - /* - * Parse header (code, id and length). - * If packet too short, drop it. - */ - if (l < HEADERLEN) { - FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.\n", - f->protocol)); - return; - } - GETCHAR(code, inp); - GETCHAR(id, inp); - GETSHORT(len, inp); - if (len < HEADERLEN) { - FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.\n", - f->protocol)); - return; - } - if (len > l) { - FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.\n", - f->protocol)); - return; - } - len -= HEADERLEN; /* subtract header length */ - - if( f->state == INITIAL || f->state == STARTING ){ - FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.\n", - f->protocol, f->state)); - return; - } - FSMDEBUG((LOG_INFO, "fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); - /* - * Action depends on code. - */ - switch (code) { - case CONFREQ: - fsm_rconfreq(f, id, inp, len); - break; - - case CONFACK: - fsm_rconfack(f, id, inp, len); - break; - - case CONFNAK: - case CONFREJ: - fsm_rconfnakrej(f, code, id, inp, len); - break; - - case TERMREQ: - fsm_rtermreq(f, id, inp, len); - break; - - case TERMACK: - fsm_rtermack(f); - break; - - case CODEREJ: - fsm_rcoderej(f, inp, len); - break; - - default: - if( !f->callbacks->extcode - || !(*f->callbacks->extcode)(f, code, id, inp, len) ) - fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); - break; - } + u8_t *inp = inpacket; + u8_t code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + if (l < HEADERLEN) { + FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.\n", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.\n", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.\n", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == LS_INITIAL || f->state == LS_STARTING ) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d (%s).\n", + f->protocol, f->state, ppperr_strerr[f->state])); + return; + } + FSMDEBUG((LOG_INFO, "fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode || + !(*f->callbacks->extcode)(f, code, id, inp, len) ) { + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + } + break; + } } @@ -405,48 +440,51 @@ void fsm_input(fsm *f, u_char *inpacket, * * Treat this as a catastrophic error (RXJ-). */ -void fsm_protreject(fsm *f) +void +fsm_protreject(fsm *f) { - switch( f->state ){ - case CLOSING: - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - /* fall through */ - case CLOSED: - f->state = CLOSED; - if( f->callbacks->finished ) - (*f->callbacks->finished)(f); - break; - - case STOPPING: - case REQSENT: - case ACKRCVD: - case ACKSENT: - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - /* fall through */ - case STOPPED: - f->state = STOPPED; - if( f->callbacks->finished ) - (*f->callbacks->finished)(f); - break; - - case OPENED: - if( f->callbacks->down ) - (*f->callbacks->down)(f); - - /* Init restart counter, send Terminate-Request */ - f->retransmits = f->maxtermtransmits; - fsm_sdata(f, TERMREQ, f->reqid = ++f->id, - (u_char *) f->term_reason, f->term_reason_len); - TIMEOUT(fsm_timeout, f, f->timeouttime); - --f->retransmits; - - f->state = STOPPING; - break; - - default: - FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!\n", - PROTO_NAME(f), f->state)); - } + switch( f->state ) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_CLOSED: + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_STOPPED: + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u8_t *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_STOPPING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } } @@ -460,379 +498,410 @@ void fsm_protreject(fsm *f) /* * fsm_timeout - Timeout expired. */ -static void fsm_timeout(void *arg) +static void +fsm_timeout(void *arg) { - fsm *f = (fsm *) arg; + fsm *f = (fsm *) arg; - switch (f->state) { - case CLOSING: - case STOPPING: - if( f->retransmits <= 0 ){ - FSMDEBUG((LOG_WARNING, "%s: timeout sending Terminate-Request state=%d\n", - PROTO_NAME(f), f->state)); - /* - * We've waited for an ack long enough. Peer probably heard us. - */ - f->state = (f->state == CLOSING)? CLOSED: STOPPED; - if( f->callbacks->finished ) - (*f->callbacks->finished)(f); - } else { - FSMDEBUG((LOG_WARNING, "%s: timeout resending Terminate-Requests state=%d\n", - PROTO_NAME(f), f->state)); - /* Send Terminate-Request */ - fsm_sdata(f, TERMREQ, f->reqid = ++f->id, - (u_char *) f->term_reason, f->term_reason_len); - TIMEOUT(fsm_timeout, f, f->timeouttime); - --f->retransmits; - } - break; + switch (f->state) { + case LS_CLOSING: + case LS_STOPPING: + if( f->retransmits <= 0 ) { + FSMDEBUG((LOG_WARNING, "%s: timeout sending Terminate-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG((LOG_WARNING, "%s: timeout resending Terminate-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u8_t *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; - case REQSENT: - case ACKRCVD: - case ACKSENT: - if (f->retransmits <= 0) { - FSMDEBUG((LOG_WARNING, "%s: timeout sending Config-Requests state=%d\n", - PROTO_NAME(f), f->state)); - f->state = STOPPED; - if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) - (*f->callbacks->finished)(f); - - } else { - FSMDEBUG((LOG_WARNING, "%s: timeout resending Config-Request state=%d\n", - PROTO_NAME(f), f->state)); - /* Retransmit the configure-request */ - if (f->callbacks->retransmit) - (*f->callbacks->retransmit)(f); - fsm_sconfreq(f, 1); /* Re-send Configure-Request */ - if( f->state == ACKRCVD ) - f->state = REQSENT; - } - break; + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + if (f->retransmits <= 0) { + FSMDEBUG((LOG_WARNING, "%s: timeout sending Config-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + f->state = LS_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG((LOG_WARNING, "%s: timeout resending Config-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) { + (*f->callbacks->retransmit)(f); + } + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } + } + break; default: - FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!\n", - PROTO_NAME(f), f->state)); - } + FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } } /* * fsm_rconfreq - Receive Configure-Request. */ -static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) +static void +fsm_rconfreq(fsm *f, u8_t id, u8_t *inp, int len) { - int code, reject_if_disagree; - - FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d state=%d\n", - PROTO_NAME(f), id, f->state)); - switch( f->state ){ - case CLOSED: - /* Go away, we're closed */ - fsm_sdata(f, TERMACK, id, NULL, 0); - return; - case CLOSING: - case STOPPING: - return; - - case OPENED: - /* Go down and restart negotiation */ - if( f->callbacks->down ) - (*f->callbacks->down)(f); /* Inform upper layers */ - fsm_sconfreq(f, 0); /* Send initial Configure-Request */ - break; - - case STOPPED: - /* Negotiation started by our peer */ - fsm_sconfreq(f, 0); /* Send initial Configure-Request */ - f->state = REQSENT; - break; - } - - /* - * Pass the requested configuration options - * to protocol-specific code for checking. - */ - if (f->callbacks->reqci){ /* Check CI */ - reject_if_disagree = (f->nakloops >= f->maxnakloops); - code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); - } - else if (len) - code = CONFREJ; /* Reject all CI */ - else - code = CONFACK; - - /* send the Ack, Nak or Rej to the peer */ - fsm_sdata(f, (u_char)code, id, inp, len); - - if (code == CONFACK) { - if (f->state == ACKRCVD) { - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - f->state = OPENED; - if (f->callbacks->up) - (*f->callbacks->up)(f); /* Inform upper layers */ - } - else - f->state = ACKSENT; - f->nakloops = 0; - } - else { - /* we sent CONFACK or CONFREJ */ - if (f->state != ACKRCVD) - f->state = REQSENT; - if( code == CONFNAK ) - ++f->nakloops; - } + int code, reject_if_disagree; + + FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + switch( f->state ) { + case LS_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case LS_CLOSING: + case LS_STOPPING: + return; + + case LS_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case LS_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci) { /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) { + code = CONFREJ; /* Reject all CI */ + } else { + code = CONFACK; + } + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, (u8_t)code, id, inp, len); + + if (code == CONFACK) { + if (f->state == LS_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + } else { + f->state = LS_ACKSENT; + } + f->nakloops = 0; + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != LS_ACKRCVD) { + f->state = LS_REQSENT; + } + if( code == CONFNAK ) { + ++f->nakloops; + } + } } /* * fsm_rconfack - Receive Configure-Ack. */ -static void fsm_rconfack(fsm *f, int id, u_char *inp, int len) +static void +fsm_rconfack(fsm *f, int id, u8_t *inp, int len) { - FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d state=%d\n", - PROTO_NAME(f), id, f->state)); - - if (id != f->reqid || f->seen_ack) /* Expected id? */ - return; /* Nope, toss... */ - if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): - (len == 0)) ){ - /* Ack is bad - ignore it */ - FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)\n", - PROTO_NAME(f), len)); - return; - } - f->seen_ack = 1; - - switch (f->state) { - case CLOSED: - case STOPPED: - fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); - break; - - case REQSENT: - f->state = ACKRCVD; - f->retransmits = f->maxconfreqtransmits; - break; - - case ACKRCVD: - /* Huh? an extra valid Ack? oh well... */ - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - fsm_sconfreq(f, 0); - f->state = REQSENT; - break; - - case ACKSENT: - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - f->state = OPENED; - f->retransmits = f->maxconfreqtransmits; - if (f->callbacks->up) - (*f->callbacks->up)(f); /* Inform upper layers */ - break; - - case OPENED: - /* Go down and restart negotiation */ - if (f->callbacks->down) - (*f->callbacks->down)(f); /* Inform upper layers */ - fsm_sconfreq(f, 0); /* Send initial Configure-Request */ - f->state = REQSENT; - break; - } + FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) { + /* Ack is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)\n", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u8_t)id, NULL, 0); + break; + + case LS_REQSENT: + f->state = LS_ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case LS_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } } /* * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. */ -static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) +static void +fsm_rconfnakrej(fsm *f, int code, int id, u8_t *inp, int len) { - int (*proc) (fsm *, u_char *, int); - int ret; - - FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d state=%d\n", - PROTO_NAME(f), id, f->state)); - - if (id != f->reqid || f->seen_ack) /* Expected id? */ - return; /* Nope, toss... */ - proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; - if (!proc || !(ret = proc(f, inp, len))) { - /* Nak/reject is bad - ignore it */ - FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)\n", - PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); - return; - } - f->seen_ack = 1; - - switch (f->state) { - case CLOSED: - case STOPPED: - fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); - break; - - case REQSENT: - case ACKSENT: - /* They didn't agree to what we wanted - try another request */ - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - if (ret < 0) - f->state = STOPPED; /* kludge for stopping CCP */ - else - fsm_sconfreq(f, 0); /* Send Configure-Request */ - break; - - case ACKRCVD: - /* Got a Nak/reject when we had already had an Ack?? oh well... */ - UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ - fsm_sconfreq(f, 0); - f->state = REQSENT; - break; - - case OPENED: - /* Go down and restart negotiation */ - if (f->callbacks->down) - (*f->callbacks->down)(f); /* Inform upper layers */ - fsm_sconfreq(f, 0); /* Send initial Configure-Request */ - f->state = REQSENT; - break; - } + int (*proc) (fsm *, u8_t *, int); + int ret; + + FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !((ret = proc(f, inp, len)))) { + /* Nak/reject is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)\n", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u8_t)id, NULL, 0); + break; + + case LS_REQSENT: + case LS_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) { + f->state = LS_STOPPED; /* kludge for stopping CCP */ + } else { + fsm_sconfreq(f, 0); /* Send Configure-Request */ + } + break; + + case LS_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } } /* * fsm_rtermreq - Receive Terminate-Req. */ -static void fsm_rtermreq(fsm *f, int id, u_char *p, int len) +static void +fsm_rtermreq(fsm *f, int id, u8_t *p, int len) { - FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d state=%d\n", - PROTO_NAME(f), id, f->state)); - - switch (f->state) { - case ACKRCVD: - case ACKSENT: - f->state = REQSENT; /* Start over but keep trying */ - break; - - case OPENED: - if (len > 0) { - FSMDEBUG((LOG_INFO, "%s terminated by peer (%x)\n", PROTO_NAME(f), p)); - } else { - FSMDEBUG((LOG_INFO, "%s terminated by peer\n", PROTO_NAME(f))); - } - if (f->callbacks->down) - (*f->callbacks->down)(f); /* Inform upper layers */ - f->retransmits = 0; - f->state = STOPPING; - TIMEOUT(fsm_timeout, f, f->timeouttime); - break; - } - - fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + LWIP_UNUSED_ARG(p); + + FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_REQSENT; /* Start over but keep trying */ + break; + + case LS_OPENED: + if (len > 0) { + FSMDEBUG((LOG_INFO, "%s terminated by peer (%x)\n", PROTO_NAME(f), p)); + } else { + FSMDEBUG((LOG_INFO, "%s terminated by peer\n", PROTO_NAME(f))); + } + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + f->retransmits = 0; + f->state = LS_STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, (u8_t)id, NULL, 0); } /* * fsm_rtermack - Receive Terminate-Ack. */ -static void fsm_rtermack(fsm *f) +static void +fsm_rtermack(fsm *f) { - FSMDEBUG((LOG_INFO, "fsm_rtermack(%s): state=%d\n", - PROTO_NAME(f), f->state)); - - switch (f->state) { - case CLOSING: - UNTIMEOUT(fsm_timeout, f); - f->state = CLOSED; - if( f->callbacks->finished ) - (*f->callbacks->finished)(f); - break; - case STOPPING: - UNTIMEOUT(fsm_timeout, f); - f->state = STOPPED; - if( f->callbacks->finished ) - (*f->callbacks->finished)(f); - break; - - case ACKRCVD: - f->state = REQSENT; - break; - - case OPENED: - if (f->callbacks->down) - (*f->callbacks->down)(f); /* Inform upper layers */ - fsm_sconfreq(f, 0); - break; - } + FSMDEBUG((LOG_INFO, "fsm_rtermack(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_ACKRCVD: + f->state = LS_REQSENT; + break; + + case LS_OPENED: + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); + break; + } } /* * fsm_rcoderej - Receive an Code-Reject. */ -static void fsm_rcoderej(fsm *f, u_char *inp, int len) +static void +fsm_rcoderej(fsm *f, u8_t *inp, int len) { - u_char code, id; - - FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s): state=%d\n", - PROTO_NAME(f), f->state)); - - if (len < HEADERLEN) { - FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!\n")); - return; - } - GETCHAR(code, inp); - GETCHAR(id, inp); - FSMDEBUG((LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d\n", - PROTO_NAME(f), code, id)); - - if( f->state == ACKRCVD ) - f->state = REQSENT; + u8_t code, id; + + FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + FSMDEBUG((LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d\n", + PROTO_NAME(f), code, id)); + + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } } /* * fsm_sconfreq - Send a Configure-Request. */ -static void fsm_sconfreq(fsm *f, int retransmit) +static void +fsm_sconfreq(fsm *f, int retransmit) { - u_char *outp; - int cilen; - - if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){ - /* Not currently negotiating - reset options */ - if( f->callbacks->resetci ) - (*f->callbacks->resetci)(f); - f->nakloops = 0; - } - - if( !retransmit ){ - /* New request - reset retransmission counter, use new ID */ - f->retransmits = f->maxconfreqtransmits; - f->reqid = ++f->id; - } - - f->seen_ack = 0; - - /* - * Make up the request packet - */ - outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; - if( f->callbacks->cilen && f->callbacks->addci ){ - cilen = (*f->callbacks->cilen)(f); - if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) - cilen = peer_mru[f->unit] - HEADERLEN; - if (f->callbacks->addci) - (*f->callbacks->addci)(f, outp, &cilen); - } else - cilen = 0; - - /* send the request to our peer */ - fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); - - /* start the retransmit timer */ - --f->retransmits; - TIMEOUT(fsm_timeout, f, f->timeouttime); - - FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d\n", - PROTO_NAME(f), f->reqid)); + u8_t *outp; + int cilen; + + if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) { + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) { + (*f->callbacks->resetci)(f); + } + f->nakloops = 0; + } + + if( !retransmit ) { + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ) { + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) { + cilen = peer_mru[f->unit] - HEADERLEN; + } + if (f->callbacks->addci) { + (*f->callbacks->addci)(f, outp, &cilen); + } + } else { + cilen = 0; + } + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d\n", + PROTO_NAME(f), f->reqid)); } #endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.h b/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/fsm.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-11-05 Guy Lancaster , Global Election Systems Inc. -* Original based on BSD code. +* Original based on BSD code. *****************************************************************************/ /* * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. @@ -48,54 +48,52 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: fsm.h,v 1.2 2006/03/29 10:33:28 asl Exp $ + * $Id: fsm.h,v 1.3 2010/01/26 11:27:49 jld Exp $ */ #ifndef FSM_H #define FSM_H - /***************************************************************************** ************************* PUBLIC DEFINITIONS ********************************* *****************************************************************************/ /* * LCP Packet header = Code, id, length. */ -#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) +#define HEADERLEN (sizeof (u8_t) + sizeof (u8_t) + sizeof (u16_t)) /* * CP (LCP, IPCP, etc.) codes. */ -#define CONFREQ 1 /* Configuration Request */ -#define CONFACK 2 /* Configuration Ack */ -#define CONFNAK 3 /* Configuration Nak */ -#define CONFREJ 4 /* Configuration Reject */ -#define TERMREQ 5 /* Termination Request */ -#define TERMACK 6 /* Termination Ack */ -#define CODEREJ 7 /* Code Reject */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ /* * Link states. */ -#define INITIAL 0 /* Down, hasn't been opened */ -#define STARTING 1 /* Down, been opened */ -#define CLOSED 2 /* Up, hasn't been opened */ -#define STOPPED 3 /* Open, waiting for down event */ -#define CLOSING 4 /* Terminating the connection, not open */ -#define STOPPING 5 /* Terminating, but open */ -#define REQSENT 6 /* We've sent a Config Request */ -#define ACKRCVD 7 /* We've received a Config Ack */ -#define ACKSENT 8 /* We've sent a Config Ack */ -#define OPENED 9 /* Connection available */ - +#define LS_INITIAL 0 /* Down, hasn't been opened */ +#define LS_STARTING 1 /* Down, been opened */ +#define LS_CLOSED 2 /* Up, hasn't been opened */ +#define LS_STOPPED 3 /* Open, waiting for down event */ +#define LS_CLOSING 4 /* Terminating the connection, not open */ +#define LS_STOPPING 5 /* Terminating, but open */ +#define LS_REQSENT 6 /* We've sent a Config Request */ +#define LS_ACKRCVD 7 /* We've received a Config Ack */ +#define LS_ACKSENT 8 /* We've sent a Config Ack */ +#define LS_OPENED 9 /* Connection available */ /* * Flags - indicate options controlling FSM operation */ -#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ -#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ -#define OPT_SILENT 4 /* Wait for peer to speak first */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ /***************************************************************************** @@ -105,55 +103,41 @@ * Each FSM is described by an fsm structure and fsm callbacks. */ typedef struct fsm { - int unit; /* Interface unit number */ - u_short protocol; /* Data Link Layer Protocol field value */ - int state; /* State */ - int flags; /* Contains option bits */ - u_char id; /* Current id */ - u_char reqid; /* Current request id */ - u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ - int timeouttime; /* Timeout time in milliseconds */ - int maxconfreqtransmits;/* Maximum Configure-Request transmissions */ - int retransmits; /* Number of retransmissions left */ - int maxtermtransmits; /* Maximum Terminate-Request transmissions */ - int nakloops; /* Number of nak loops since last ack */ - int maxnakloops; /* Maximum number of nak loops tolerated */ - struct fsm_callbacks* callbacks;/* Callback routines */ - char* term_reason; /* Reason for closing protocol */ - int term_reason_len; /* Length of term_reason */ + int unit; /* Interface unit number */ + u16_t protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u8_t id; /* Current id */ + u8_t reqid; /* Current request id */ + u8_t seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks* callbacks; /* Callback routines */ + char* term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ } fsm; typedef struct fsm_callbacks { - void (*resetci) /* Reset our Configuration Information */ - (fsm*); - int (*cilen) /* Length of our Configuration Information */ - (fsm*); - void (*addci) /* Add our Configuration Information */ - (fsm*, u_char*, int*); - int (*ackci) /* ACK our Configuration Information */ - (fsm*, u_char*, int); - int (*nakci) /* NAK our Configuration Information */ - (fsm*, u_char*, int); - int (*rejci) /* Reject our Configuration Information */ - (fsm*, u_char*, int); - int (*reqci) /* Request peer's Configuration Information */ - (fsm*, u_char*, int*, int); - void (*up) /* Called when fsm reaches OPENED state */ - (fsm*); - void (*down) /* Called when fsm leaves OPENED state */ - (fsm*); - void (*starting) /* Called when we want the lower layer */ - (fsm*); - void (*finished) /* Called when we don't want the lower layer */ - (fsm*); - void (*protreject) /* Called when Protocol-Reject received */ - (int); - void (*retransmit) /* Retransmission is necessary */ - (fsm*); - int (*extcode) /* Called when unknown code received */ - (fsm*, int, u_char, u_char*, int); - char *proto_name; /* String name for protocol (for messages) */ + void (*resetci)(fsm*); /* Reset our Configuration Information */ + int (*cilen)(fsm*); /* Length of our Configuration Information */ + void (*addci)(fsm*, u8_t*, int*); /* Add our Configuration Information */ + int (*ackci)(fsm*, u8_t*, int); /* ACK our Configuration Information */ + int (*nakci)(fsm*, u8_t*, int); /* NAK our Configuration Information */ + int (*rejci)(fsm*, u8_t*, int); /* Reject our Configuration Information */ + int (*reqci)(fsm*, u8_t*, int*, int); /* Request peer's Configuration Information */ + void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */ + void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */ + void (*starting)(fsm*); /* Called when we want the lower layer */ + void (*finished)(fsm*); /* Called when we don't want the lower layer */ + void (*protreject)(int); /* Called when Protocol-Reject received */ + void (*retransmit)(fsm*); /* Retransmission is necessary */ + int (*extcode)(fsm*, int, u8_t, u8_t*, int); /* Called when unknown code received */ + char *proto_name; /* String name for protocol (for messages) */ } fsm_callbacks; @@ -163,7 +147,7 @@ typedef struct fsm_callbacks { /* * Variables */ -extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ /***************************************************************************** @@ -178,10 +162,8 @@ void fsm_lowerup (fsm*); void fsm_lowerdown (fsm*); void fsm_open (fsm*); void fsm_close (fsm*, char*); -void fsm_input (fsm*, u_char*, int); +void fsm_input (fsm*, u8_t*, int); void fsm_protreject (fsm*); -void fsm_sdata (fsm*, u_char, u_char, u_char*, int); - +void fsm_sdata (fsm*, u8_t, u8_t, u8_t*, int); #endif /* FSM_H */ - diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.c b/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-08 Guy Lancaster , Global Election Systems Inc. -* Original. +* Original. *****************************************************************************/ /* * ipcp.c - PPP IP Control Protocol. @@ -49,30 +49,33 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#include +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" -#if PPP_SUPPORT > 0 +#include "netif/ppp/ppp.h" +#include "pppdebug.h" + #include "auth.h" #include "fsm.h" #include "vj.h" #include "ipcp.h" -#include "pppdebug.h" +#include /*************************/ /*** LOCAL DEFINITIONS ***/ /*************************/ -/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */ +/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */ /* * Lengths of configuration options. */ -#define CILEN_VOID 2 -#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ -#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ -#define CILEN_ADDR 6 /* new-style single address option */ -#define CILEN_ADDRS 10 /* old-style dual address option */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ @@ -82,19 +85,19 @@ /* * Callbacks for fsm code. (CI = Configuration Information) */ -static void ipcp_resetci (fsm *); /* Reset our CI */ -static int ipcp_cilen (fsm *); /* Return length of our CI */ -static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */ -static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */ -static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */ -static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */ -static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */ -static void ipcp_up (fsm *); /* We're UP */ -static void ipcp_down (fsm *); /* We're DOWN */ +static void ipcp_resetci (fsm *); /* Reset our CI */ +static int ipcp_cilen (fsm *); /* Return length of our CI */ +static void ipcp_addci (fsm *, u8_t *, int *); /* Add our CI */ +static int ipcp_ackci (fsm *, u8_t *, int); /* Peer ack'd our CI */ +static int ipcp_nakci (fsm *, u8_t *, int); /* Peer nak'd our CI */ +static int ipcp_rejci (fsm *, u8_t *, int); /* Peer rej'd our CI */ +static int ipcp_reqci (fsm *, u8_t *, int *, int); /* Rcv CI */ +static void ipcp_up (fsm *); /* We're UP */ +static void ipcp_down (fsm *); /* We're DOWN */ #if 0 static void ipcp_script (fsm *, char *); /* Run an up/down script */ #endif -static void ipcp_finished (fsm *); /* Don't need lower layer */ +static void ipcp_finished (fsm *); /* Don't need lower layer */ /* * Protocol entry points from main code. @@ -104,13 +107,13 @@ static void ipcp_open (int); static void ipcp_close (int, char *); static void ipcp_lowerup (int); static void ipcp_lowerdown (int); -static void ipcp_input (int, u_char *, int); +static void ipcp_input (int, u8_t *, int); static void ipcp_protrej (int); static void ipcp_clear_addrs (int); -#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ - (x) == CONFNAK ? "NAK" : "REJ") +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") @@ -118,32 +121,32 @@ static void ipcp_clear_addrs (int); /*** PUBLIC DATA STRUCTURES ***/ /******************************/ /* global vars */ -ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ -ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ -ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ -ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ -fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ struct protent ipcp_protent = { - PPP_IPCP, - ipcp_init, - ipcp_input, - ipcp_protrej, - ipcp_lowerup, - ipcp_lowerdown, - ipcp_open, - ipcp_close, + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, #if 0 - ipcp_printpkt, - NULL, + ipcp_printpkt, + NULL, #endif - 1, - "IPCP", + 1, + "IPCP", #if 0 - ip_check_options, - NULL, - ip_active_pkt + ip_check_options, + NULL, + ip_active_pkt #endif }; @@ -153,25 +156,25 @@ struct protent ipcp_protent = { /*** LOCAL DATA STRUCTURES ***/ /*****************************/ /* local vars */ -static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ -static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ - ipcp_resetci, /* Reset our Configuration Information */ - ipcp_cilen, /* Length of our Configuration Information */ - ipcp_addci, /* Add our Configuration Information */ - ipcp_ackci, /* ACK our Configuration Information */ - ipcp_nakci, /* NAK our Configuration Information */ - ipcp_rejci, /* Reject our Configuration Information */ - ipcp_reqci, /* Request peer's Configuration Information */ - ipcp_up, /* Called when fsm reaches OPENED state */ - ipcp_down, /* Called when fsm leaves OPENED state */ - NULL, /* Called when we want the lower layer up */ - ipcp_finished, /* Called when we want the lower layer down */ - NULL, /* Called when Protocol-Reject received */ - NULL, /* Retransmission is necessary */ - NULL, /* Called to handle protocol-specific codes */ - "IPCP" /* String name of protocol */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches LS_OPENED state */ + ipcp_down, /* Called when fsm leaves LS_OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ }; @@ -183,13 +186,14 @@ static fsm_callbacks ipcp_callbacks = { /* * Non-standard inet_ntoa left here for compat with original ppp * sources. Assumes u32_t instead of struct in_addr. - */ + */ -char * _inet_ntoa(u32_t n) +char * +_inet_ntoa(u32_t n) { - struct in_addr ia; - ia.s_addr = n; - return inet_ntoa(ia); + struct in_addr ia; + ia.s_addr = n; + return inet_ntoa(ia); } #define inet_ntoa _inet_ntoa @@ -197,88 +201,92 @@ char * _inet_ntoa(u32_t n) /* * ipcp_init - Initialize IPCP. */ -static void ipcp_init(int unit) +static void +ipcp_init(int unit) { - fsm *f = &ipcp_fsm[unit]; - ipcp_options *wo = &ipcp_wantoptions[unit]; - ipcp_options *ao = &ipcp_allowoptions[unit]; - - f->unit = unit; - f->protocol = PPP_IPCP; - f->callbacks = &ipcp_callbacks; - fsm_init(&ipcp_fsm[unit]); - - memset(wo, 0, sizeof(*wo)); - memset(ao, 0, sizeof(*ao)); - - wo->neg_addr = 1; - wo->ouraddr = 0; -#if VJ_SUPPORT > 0 - wo->neg_vj = 1; -#else - wo->neg_vj = 0; -#endif - wo->vj_protocol = IPCP_VJ_COMP; - wo->maxslotindex = MAX_SLOTS - 1; - wo->cflag = 0; - - wo->default_route = 1; - - ao->neg_addr = 1; -#if VJ_SUPPORT > 0 - ao->neg_vj = 1; -#else - ao->neg_vj = 0; -#endif - ao->maxslotindex = MAX_SLOTS - 1; - ao->cflag = 1; - - ao->default_route = 1; + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->ouraddr = 0; +#if VJ_SUPPORT + wo->neg_vj = 1; +#else /* VJ_SUPPORT */ + wo->neg_vj = 0; +#endif /* VJ_SUPPORT */ + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_SLOTS - 1; + wo->cflag = 0; + wo->default_route = 1; + + ao->neg_addr = 1; +#if VJ_SUPPORT + ao->neg_vj = 1; +#else /* VJ_SUPPORT */ + ao->neg_vj = 0; +#endif /* VJ_SUPPORT */ + ao->maxslotindex = MAX_SLOTS - 1; + ao->cflag = 1; + ao->default_route = 1; } /* * ipcp_open - IPCP is allowed to come up. */ -static void ipcp_open(int unit) +static void +ipcp_open(int unit) { - fsm_open(&ipcp_fsm[unit]); + fsm_open(&ipcp_fsm[unit]); } /* * ipcp_close - Take IPCP down. */ -static void ipcp_close(int unit, char *reason) +static void +ipcp_close(int unit, char *reason) { - fsm_close(&ipcp_fsm[unit], reason); + fsm_close(&ipcp_fsm[unit], reason); } /* * ipcp_lowerup - The lower layer is up. */ -static void ipcp_lowerup(int unit) +static void +ipcp_lowerup(int unit) { - fsm_lowerup(&ipcp_fsm[unit]); + fsm_lowerup(&ipcp_fsm[unit]); } /* * ipcp_lowerdown - The lower layer is down. */ -static void ipcp_lowerdown(int unit) +static void +ipcp_lowerdown(int unit) { - fsm_lowerdown(&ipcp_fsm[unit]); + fsm_lowerdown(&ipcp_fsm[unit]); } /* * ipcp_input - Input IPCP packet. */ -static void ipcp_input(int unit, u_char *p, int len) +static void +ipcp_input(int unit, u8_t *p, int len) { - fsm_input(&ipcp_fsm[unit], p, len); + fsm_input(&ipcp_fsm[unit], p, len); } @@ -287,142 +295,151 @@ static void ipcp_input(int unit, u_char * * Pretend the lower layer went down, so we shut up. */ -static void ipcp_protrej(int unit) +static void +ipcp_protrej(int unit) { - fsm_lowerdown(&ipcp_fsm[unit]); + fsm_lowerdown(&ipcp_fsm[unit]); } /* * ipcp_resetci - Reset our CI. */ -static void ipcp_resetci(fsm *f) +static void +ipcp_resetci(fsm *f) { - ipcp_options *wo = &ipcp_wantoptions[f->unit]; - - wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; - if (wo->ouraddr == 0) - wo->accept_local = 1; - if (wo->hisaddr == 0) - wo->accept_remote = 1; - /* Request DNS addresses from the peer */ - wo->req_dns1 = ppp_settings.usepeerdns; - wo->req_dns2 = ppp_settings.usepeerdns; - ipcp_gotoptions[f->unit] = *wo; - cis_received[f->unit] = 0; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) { + wo->accept_local = 1; + } + if (wo->hisaddr == 0) { + wo->accept_remote = 1; + } + /* Request DNS addresses from the peer */ + wo->req_dns1 = ppp_settings.usepeerdns; + wo->req_dns2 = ppp_settings.usepeerdns; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; } /* * ipcp_cilen - Return length of our CI. */ -static int ipcp_cilen(fsm *f) +static int +ipcp_cilen(fsm *f) { - ipcp_options *go = &ipcp_gotoptions[f->unit]; - ipcp_options *wo = &ipcp_wantoptions[f->unit]; - ipcp_options *ho = &ipcp_hisoptions[f->unit]; - -#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) -#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) -#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) - - /* - * First see if we want to change our options to the old - * forms because we have received old forms from the peer. - */ - if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { - /* use the old style of address negotiation */ - go->neg_addr = 1; - go->old_addrs = 1; - } - if (wo->neg_vj && !go->neg_vj && !go->old_vj) { - /* try an older style of VJ negotiation */ - if (cis_received[f->unit] == 0) { - /* keep trying the new style until we see some CI from the peer */ - go->neg_vj = 1; - } else { - /* use the old style only if the peer did */ - if (ho->neg_vj && ho->old_vj) { - go->neg_vj = 1; - go->old_vj = 1; - go->vj_protocol = ho->vj_protocol; - } - } - } - - return (LENCIADDR(go->neg_addr, go->old_addrs) - + LENCIVJ(go->neg_vj, go->old_vj) + - LENCIDNS(go->req_dns1) + - LENCIDNS(go->req_dns2)); + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) +#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2)); } /* * ipcp_addci - Add our desired CIs to a packet. */ -static void ipcp_addci(fsm *f, u_char *ucp, int *lenp) +static void +ipcp_addci(fsm *f, u8_t *ucp, int *lenp) { - ipcp_options *go = &ipcp_gotoptions[f->unit]; - int len = *lenp; - + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + #define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ - if (neg) { \ - int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ - if (len >= vjlen) { \ - PUTCHAR(opt, ucp); \ - PUTCHAR(vjlen, ucp); \ - PUTSHORT(val, ucp); \ - if (!old) { \ - PUTCHAR(maxslotindex, ucp); \ - PUTCHAR(cflag, ucp); \ - } \ - len -= vjlen; \ - } else \ - neg = 0; \ - } - + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else { \ + neg = 0; \ + } \ + } + #define ADDCIADDR(opt, neg, old, val1, val2) \ - if (neg) { \ - int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ - if (len >= addrlen) { \ - u32_t l; \ - PUTCHAR(opt, ucp); \ - PUTCHAR(addrlen, ucp); \ - l = ntohl(val1); \ - PUTLONG(l, ucp); \ - if (old) { \ - l = ntohl(val2); \ - PUTLONG(l, ucp); \ - } \ - len -= addrlen; \ - } else \ - neg = 0; \ - } + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else { \ + neg = 0; \ + } \ + } #define ADDCIDNS(opt, neg, addr) \ - if (neg) { \ - if (len >= CILEN_ADDR) { \ - u32_t l; \ - PUTCHAR(opt, ucp); \ - PUTCHAR(CILEN_ADDR, ucp); \ - l = ntohl(addr); \ - PUTLONG(l, ucp); \ - len -= CILEN_ADDR; \ - } else \ - neg = 0; \ - } - - ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, - go->old_addrs, go->ouraddr, go->hisaddr); - - ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, - go->maxslotindex, go->cflag); - - ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else { \ + neg = 0; \ + } \ + } - ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); - *lenp -= len; + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + *lenp -= len; } @@ -430,406 +447,443 @@ static void ipcp_addci(fsm *f, u_char *u * ipcp_ackci - Ack our CIs. * * Returns: - * 0 - Ack was bad. - * 1 - Ack was good. + * 0 - Ack was bad. + * 1 - Ack was good. */ -static int ipcp_ackci(fsm *f, u_char *p, int len) +static int +ipcp_ackci(fsm *f, u8_t *p, int len) { - ipcp_options *go = &ipcp_gotoptions[f->unit]; - u_short cilen, citype, cishort; - u32_t cilong; - u_char cimaxslotindex, cicflag; - - /* - * CIs must be in exactly the same order that we sent... - * Check packet length and CI length at each step. - * If we find any deviations, then this packet is bad. - */ - + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u16_t cilen, citype, cishort; + u32_t cilong; + u8_t cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + #define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ - if (neg) { \ - int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ - if ((len -= vjlen) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != vjlen || \ - citype != opt) \ - goto bad; \ - GETSHORT(cishort, p); \ - if (cishort != val) \ - goto bad; \ - if (!old) { \ - GETCHAR(cimaxslotindex, p); \ - if (cimaxslotindex != maxslotindex) \ - goto bad; \ - GETCHAR(cicflag, p); \ - if (cicflag != cflag) \ - goto bad; \ - } \ - } - + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETSHORT(cishort, p); \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) { \ + goto bad; \ + } \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) { \ + goto bad; \ + } \ + } \ + } + #define ACKCIADDR(opt, neg, old, val1, val2) \ - if (neg) { \ - int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ - u32_t l; \ - if ((len -= addrlen) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != addrlen || \ - citype != opt) \ - goto bad; \ - GETLONG(l, p); \ - cilong = htonl(l); \ - if (val1 != cilong) \ - goto bad; \ - if (old) { \ - GETLONG(l, p); \ - cilong = htonl(l); \ - if (val2 != cilong) \ - goto bad; \ - } \ - } + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u32_t l; \ + if ((len -= addrlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) { \ + goto bad; \ + } \ + } \ + } #define ACKCIDNS(opt, neg, addr) \ - if (neg) { \ - u32_t l; \ - if ((len -= CILEN_ADDR) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != CILEN_ADDR || \ - citype != opt) \ - goto bad; \ - GETLONG(l, p); \ - cilong = htonl(l); \ - if (addr != cilong) \ - goto bad; \ - } - - ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, - go->old_addrs, go->ouraddr, go->hisaddr); - - ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, - go->maxslotindex, go->cflag); - - ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) { \ + goto bad; \ + } \ + } - ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); - /* - * If there are any remaining CIs, then this packet is bad. - */ - if (len != 0) - goto bad; - return (1); - + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + return (1); + bad: - IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!\n")); - return (0); + IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!\n")); + return (0); } /* * ipcp_nakci - Peer has sent a NAK for some of our CIs. * This should not modify any state if the Nak is bad - * or if IPCP is in the OPENED state. + * or if IPCP is in the LS_OPENED state. * * Returns: - * 0 - Nak was bad. - * 1 - Nak was good. + * 0 - Nak was bad. + * 1 - Nak was good. */ -static int ipcp_nakci(fsm *f, u_char *p, int len) +static int +ipcp_nakci(fsm *f, u8_t *p, int len) { - ipcp_options *go = &ipcp_gotoptions[f->unit]; - u_char cimaxslotindex, cicflag; - u_char citype, cilen, *next; - u_short cishort; - u32_t ciaddr1, ciaddr2, l, cidnsaddr; - ipcp_options no; /* options we've seen Naks for */ - ipcp_options try; /* options to request next time */ - - BZERO(&no, sizeof(no)); - try = *go; - - /* - * Any Nak'd CIs must be in exactly the same order that we sent. - * Check packet length and CI length at each step. - * If we find any deviations, then this packet is bad. - */ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u8_t cimaxslotindex, cicflag; + u8_t citype, cilen, *next; + u16_t cishort; + u32_t ciaddr1, ciaddr2, l, cidnsaddr; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ #define NAKCIADDR(opt, neg, old, code) \ - if (go->neg && \ - len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ - p[1] == cilen && \ - p[0] == opt) { \ - len -= cilen; \ - INCPTR(2, p); \ - GETLONG(l, p); \ - ciaddr1 = htonl(l); \ - if (old) { \ - GETLONG(l, p); \ - ciaddr2 = htonl(l); \ - no.old_addrs = 1; \ - } else \ - ciaddr2 = 0; \ - no.neg = 1; \ - code \ - } - + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else { \ + ciaddr2 = 0; \ + } \ + no.neg = 1; \ + code \ + } + #define NAKCIVJ(opt, neg, code) \ - if (go->neg && \ - ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ - len >= cilen && \ - p[0] == opt) { \ - len -= cilen; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - no.neg = 1; \ - code \ - } - + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + #define NAKCIDNS(opt, neg, code) \ - if (go->neg && \ - ((cilen = p[1]) == CILEN_ADDR) && \ - len >= cilen && \ - p[0] == opt) { \ - len -= cilen; \ - INCPTR(2, p); \ - GETLONG(l, p); \ - cidnsaddr = htonl(l); \ - no.neg = 1; \ - code \ - } - - /* - * Accept the peer's idea of {our,his} address, if different - * from our idea, only if the accept_{local,remote} flag is set. - */ - NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, - if (go->accept_local && ciaddr1) { /* Do we know our address? */ - try.ouraddr = ciaddr1; - IPCPDEBUG((LOG_INFO, "local IP address %s\n", - inet_ntoa(ciaddr1))); - } - if (go->accept_remote && ciaddr2) { /* Does he know his? */ - try.hisaddr = ciaddr2; - IPCPDEBUG((LOG_INFO, "remote IP address %s\n", - inet_ntoa(ciaddr2))); - } - ); - - /* - * Accept the peer's value of maxslotindex provided that it - * is less than what we asked for. Turn off slot-ID compression - * if the peer wants. Send old-style compress-type option if - * the peer wants. - */ - NAKCIVJ(CI_COMPRESSTYPE, neg_vj, - if (cilen == CILEN_VJ) { - GETCHAR(cimaxslotindex, p); - GETCHAR(cicflag, p); - if (cishort == IPCP_VJ_COMP) { - try.old_vj = 0; - if (cimaxslotindex < go->maxslotindex) - try.maxslotindex = cimaxslotindex; - if (!cicflag) - try.cflag = 0; - } else { - try.neg_vj = 0; - } - } else { - if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { - try.old_vj = 1; - try.vj_protocol = cishort; - } else { - try.neg_vj = 0; - } - } - ); - - NAKCIDNS(CI_MS_DNS1, req_dns1, - try.dnsaddr[0] = cidnsaddr; - IPCPDEBUG((LOG_INFO, "primary DNS address %s\n", inet_ntoa(cidnsaddr))); - ); + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = htonl(l); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "local IP address %s\n", + inet_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG((LOG_INFO, "remote IP address %s\n", + inet_ntoa(ciaddr2))); + } + ); - NAKCIDNS(CI_MS_DNS2, req_dns2, - try.dnsaddr[1] = cidnsaddr; - IPCPDEBUG((LOG_INFO, "secondary DNS address %s\n", inet_ntoa(cidnsaddr))); - ); + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) { + try.maxslotindex = cimaxslotindex; + } + if (!cicflag) { + try.cflag = 0; + } + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + try.dnsaddr[0] = cidnsaddr; + IPCPDEBUG((LOG_INFO, "primary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + try.dnsaddr[1] = cidnsaddr; + IPCPDEBUG((LOG_INFO, "secondary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); - /* - * There may be remaining CIs, if the peer is requesting negotiation - * on an option that we didn't include in our request packet. - * If they want to negotiate about IP addresses, we comply. - * If they want us to ask for compression, we refuse. - */ - while (len > CILEN_VOID) { - GETCHAR(citype, p); - GETCHAR(cilen, p); - if( (len -= cilen) < 0 ) - goto bad; - next = p + cilen - 2; - - switch (citype) { - case CI_COMPRESSTYPE: - if (go->neg_vj || no.neg_vj || - (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) - goto bad; - no.neg_vj = 1; - break; - case CI_ADDRS: - if ((go->neg_addr && go->old_addrs) || no.old_addrs - || cilen != CILEN_ADDRS) - goto bad; - try.neg_addr = 1; - try.old_addrs = 1; - GETLONG(l, p); - ciaddr1 = htonl(l); - if (ciaddr1 && go->accept_local) - try.ouraddr = ciaddr1; - GETLONG(l, p); - ciaddr2 = htonl(l); - if (ciaddr2 && go->accept_remote) - try.hisaddr = ciaddr2; - no.old_addrs = 1; - break; - case CI_ADDR: - if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) - goto bad; - try.old_addrs = 0; - GETLONG(l, p); - ciaddr1 = htonl(l); - if (ciaddr1 && go->accept_local) - try.ouraddr = ciaddr1; - if (try.ouraddr != 0) - try.neg_addr = 1; - no.neg_addr = 1; - break; - } - p = next; - } - - /* If there is still anything left, this packet is bad. */ - if (len != 0) - goto bad; - - /* - * OK, the Nak is good. Now we can update state. - */ - if (f->state != OPENED) - *go = try; - - return 1; - + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + goto bad; + } + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) { + goto bad; + } + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) { + try.hisaddr = ciaddr2; + } + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) { + goto bad; + } + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + if (try.ouraddr != 0) { + try.neg_addr = 1; + } + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + + return 1; + bad: - IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!\n")); - return 0; + IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!\n")); + return 0; } /* * ipcp_rejci - Reject some of our CIs. */ -static int ipcp_rejci(fsm *f, u_char *p, int len) +static int +ipcp_rejci(fsm *f, u8_t *p, int len) { - ipcp_options *go = &ipcp_gotoptions[f->unit]; - u_char cimaxslotindex, ciflag, cilen; - u_short cishort; - u32_t cilong; - ipcp_options try; /* options to request next time */ - - try = *go; - /* - * Any Rejected CIs must be in exactly the same order that we sent. - * Check packet length and CI length at each step. - * If we find any deviations, then this packet is bad. - */ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u8_t cimaxslotindex, ciflag, cilen; + u16_t cishort; + u32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ #define REJCIADDR(opt, neg, old, val1, val2) \ - if (go->neg && \ - len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ - p[1] == cilen && \ - p[0] == opt) { \ - u32_t l; \ - len -= cilen; \ - INCPTR(2, p); \ - GETLONG(l, p); \ - cilong = htonl(l); \ - /* Check rejected value. */ \ - if (cilong != val1) \ - goto bad; \ - if (old) { \ - GETLONG(l, p); \ - cilong = htonl(l); \ - /* Check rejected value. */ \ - if (cilong != val2) \ - goto bad; \ - } \ - try.neg = 0; \ - } - + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + #define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ - if (go->neg && \ - p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ - len >= p[1] && \ - p[0] == opt) { \ - len -= p[1]; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - /* Check rejected value. */ \ - if (cishort != val) \ - goto bad; \ - if (!old) { \ - GETCHAR(cimaxslotindex, p); \ - if (cimaxslotindex != maxslot) \ - goto bad; \ - GETCHAR(ciflag, p); \ - if (ciflag != cflag) \ - goto bad; \ - } \ - try.neg = 0; \ - } - + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) { \ + goto bad; \ + } \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + #define REJCIDNS(opt, neg, dnsaddr) \ - if (go->neg && \ - ((cilen = p[1]) == CILEN_ADDR) && \ - len >= cilen && \ - p[0] == opt) { \ - u32_t l; \ - len -= cilen; \ - INCPTR(2, p); \ - GETLONG(l, p); \ - cilong = htonl(l); \ - /* Check rejected value. */ \ - if (cilong != dnsaddr) \ - goto bad; \ - try.neg = 0; \ - } + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) { \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); - REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, - go->old_addrs, go->ouraddr, go->hisaddr); - - REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, - go->maxslotindex, go->cflag); - - REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); - REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); - /* - * If there are any remaining CIs, then this packet is bad. - */ - if (len != 0) - goto bad; - /* - * Now we can update state. - */ - if (f->state != OPENED) - *go = try; - return 1; - + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + bad: - IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!\n")); - return 0; + IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!\n")); + return 0; } @@ -840,328 +894,327 @@ bad: * appropriately. If reject_if_disagree is non-zero, doesn't return * CONFNAK; returns CONFREJ if it can't return CONFACK. */ -static int ipcp_reqci( - fsm *f, - u_char *inp, /* Requested CIs */ - int *len, /* Length of requested CIs */ - int reject_if_disagree -) +static int +ipcp_reqci(fsm *f, u8_t *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree) { - ipcp_options *wo = &ipcp_wantoptions[f->unit]; - ipcp_options *ho = &ipcp_hisoptions[f->unit]; - ipcp_options *ao = &ipcp_allowoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; #ifdef OLD_CI_ADDRS - ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; #endif - u_char *cip, *next; /* Pointer to current and next CIs */ - u_short cilen, citype; /* Parsed len, type */ - u_short cishort; /* Parsed short value */ - u32_t tl, ciaddr1; /* Parsed address values */ + u8_t *cip, *next; /* Pointer to current and next CIs */ + u16_t cilen, citype; /* Parsed len, type */ + u16_t cishort; /* Parsed short value */ + u32_t tl, ciaddr1; /* Parsed address values */ #ifdef OLD_CI_ADDRS - u32_t ciaddr2; /* Parsed address values */ + u32_t ciaddr2; /* Parsed address values */ #endif - int rc = CONFACK; /* Final packet return code */ - int orc; /* Individual option return code */ - u_char *p; /* Pointer to next char to parse */ - u_char *ucp = inp; /* Pointer to current output char */ - int l = *len; /* Length left */ - u_char maxslotindex, cflag; - int d; - - cis_received[f->unit] = 1; - - /* - * Reset all his options. - */ - BZERO(ho, sizeof(*ho)); - - /* - * Process all his options. - */ - next = inp; - while (l) { - orc = CONFACK; /* Assume success */ - cip = p = next; /* Remember begining of CI */ - if (l < 2 || /* Not enough data for CI header or */ - p[1] < 2 || /* CI length too small or */ - p[1] > l) { /* CI length too big? */ - IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!\n")); - orc = CONFREJ; /* Reject bad CI */ - cilen = l; /* Reject till end of packet */ - l = 0; /* Don't loop again */ - goto endswitch; - } - GETCHAR(citype, p); /* Parse CI type */ - GETCHAR(cilen, p); /* Parse CI length */ - l -= cilen; /* Adjust remaining length */ - next += cilen; /* Step to next CI */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u8_t *p; /* Pointer to next char to parse */ + u8_t *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u8_t maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); - switch (citype) { /* Check CI type */ + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ #ifdef OLD_CI_ADDRS /* Need to save space... */ - case CI_ADDRS: - IPCPDEBUG((LOG_INFO, "ipcp_reqci: received ADDRS\n")); - if (!ao->neg_addr || - cilen != CILEN_ADDRS) { /* Check CI length */ - orc = CONFREJ; /* Reject CI */ - break; - } - - /* - * If he has no address, or if we both have his address but - * disagree about it, then NAK it with our idea. - * In particular, if we don't know his address, but he does, - * then accept it. - */ - GETLONG(tl, p); /* Parse source address (his) */ - ciaddr1 = htonl(tl); - IPCPDEBUG((LOG_INFO, "his addr %s\n", inet_ntoa(ciaddr1))); - if (ciaddr1 != wo->hisaddr - && (ciaddr1 == 0 || !wo->accept_remote)) { - orc = CONFNAK; - if (!reject_if_disagree) { - DECPTR(sizeof(u32_t), p); - tl = ntohl(wo->hisaddr); - PUTLONG(tl, p); - } - } else if (ciaddr1 == 0 && wo->hisaddr == 0) { - /* - * If neither we nor he knows his address, reject the option. - */ - orc = CONFREJ; - wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ - break; - } - - /* - * If he doesn't know our address, or if we both have our address - * but disagree about it, then NAK it with our idea. - */ - GETLONG(tl, p); /* Parse desination address (ours) */ - ciaddr2 = htonl(tl); - IPCPDEBUG((LOG_INFO, "our addr %s\n", inet_ntoa(ciaddr2))); - if (ciaddr2 != wo->ouraddr) { - if (ciaddr2 == 0 || !wo->accept_local) { - orc = CONFNAK; - if (!reject_if_disagree) { - DECPTR(sizeof(u32_t), p); - tl = ntohl(wo->ouraddr); - PUTLONG(tl, p); - } - } else { - go->ouraddr = ciaddr2; /* accept peer's idea */ - } - } - - ho->neg_addr = 1; - ho->old_addrs = 1; - ho->hisaddr = ciaddr1; - ho->ouraddr = ciaddr2; - break; + case CI_ADDRS: + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received ADDRS\n")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "his addr %s\n", inet_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG((LOG_INFO, "our addr %s\n", inet_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; #endif - - case CI_ADDR: - if (!ao->neg_addr) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR not allowed\n")); - orc = CONFREJ; /* Reject CI */ - break; - } else if (cilen != CILEN_ADDR) { /* Check CI length */ - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR bad len\n")); - orc = CONFREJ; /* Reject CI */ - break; - } - - /* - * If he has no address, or if we both have his address but - * disagree about it, then NAK it with our idea. - * In particular, if we don't know his address, but he does, - * then accept it. - */ - GETLONG(tl, p); /* Parse source address (his) */ - ciaddr1 = htonl(tl); - if (ciaddr1 != wo->hisaddr - && (ciaddr1 == 0 || !wo->accept_remote)) { - orc = CONFNAK; - if (!reject_if_disagree) { - DECPTR(sizeof(u32_t), p); - tl = ntohl(wo->hisaddr); - PUTLONG(tl, p); - } - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1))); - } else if (ciaddr1 == 0 && wo->hisaddr == 0) { - /* - * Don't ACK an address of 0.0.0.0 - reject it instead. - */ - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1))); - orc = CONFREJ; - wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ - break; - } - - ho->neg_addr = 1; - ho->hisaddr = ciaddr1; - IPCPDEBUG((LOG_INFO, "ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1))); - break; - - case CI_MS_DNS1: - case CI_MS_DNS2: - /* Microsoft primary or secondary DNS request */ - d = citype == CI_MS_DNS2; - - /* If we do not have a DNS address then we cannot send it */ - if (ao->dnsaddr[d] == 0 || - cilen != CILEN_ADDR) { /* Check CI length */ - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting DNS%d Request\n", d+1)); - orc = CONFREJ; /* Reject CI */ - break; - } - GETLONG(tl, p); - if (htonl(tl) != ao->dnsaddr[d]) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking DNS%d Request %d\n", - d+1, inet_ntoa(tl))); - DECPTR(sizeof(u32_t), p); - tl = ntohl(ao->dnsaddr[d]); - PUTLONG(tl, p); - orc = CONFNAK; - } - IPCPDEBUG((LOG_INFO, "ipcp_reqci: received DNS%d Request\n", d+1)); - break; - - case CI_MS_WINS1: - case CI_MS_WINS2: - /* Microsoft primary or secondary WINS request */ - d = citype == CI_MS_WINS2; - IPCPDEBUG((LOG_INFO, "ipcp_reqci: received WINS%d Request\n", d+1)); - - /* If we do not have a DNS address then we cannot send it */ - if (ao->winsaddr[d] == 0 || - cilen != CILEN_ADDR) { /* Check CI length */ - orc = CONFREJ; /* Reject CI */ - break; - } - GETLONG(tl, p); - if (htonl(tl) != ao->winsaddr[d]) { - DECPTR(sizeof(u32_t), p); - tl = ntohl(ao->winsaddr[d]); - PUTLONG(tl, p); - orc = CONFNAK; - } - break; - - case CI_COMPRESSTYPE: - if (!ao->neg_vj) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n")); - orc = CONFREJ; - break; - } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen)); - orc = CONFREJ; - break; - } - GETSHORT(cishort, p); - - if (!(cishort == IPCP_VJ_COMP || - (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort)); - orc = CONFREJ; - break; - } - - ho->neg_vj = 1; - ho->vj_protocol = cishort; - if (cilen == CILEN_VJ) { - GETCHAR(maxslotindex, p); - if (maxslotindex > ao->maxslotindex) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ max slot %d\n", maxslotindex)); - orc = CONFNAK; - if (!reject_if_disagree){ - DECPTR(1, p); - PUTCHAR(ao->maxslotindex, p); - } - } - GETCHAR(cflag, p); - if (cflag && !ao->cflag) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ cflag %d\n", cflag)); - orc = CONFNAK; - if (!reject_if_disagree){ - DECPTR(1, p); - PUTCHAR(wo->cflag, p); - } - } - ho->maxslotindex = maxslotindex; - ho->cflag = cflag; - } else { - ho->old_vj = 1; - ho->maxslotindex = MAX_SLOTS - 1; - ho->cflag = 1; - } - IPCPDEBUG((LOG_INFO, - "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n", - ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag)); - break; - - default: - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting unknown CI type %d\n", citype)); - orc = CONFREJ; - break; - } - + + case CI_ADDR: + if (!ao->neg_addr) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR bad len\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1))); + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1))); + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1))); + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting DNS%d Request\n", d+1)); + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking DNS%d Request %d\n", + d+1, inet_ntoa(tl))); + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received DNS%d Request\n", d+1)); + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received WINS%d Request\n", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + if (!ao->neg_vj) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen)); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort)); + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ max slot %d\n", maxslotindex)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ cflag %d\n", cflag)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_SLOTS - 1; + ho->cflag = 1; + } + IPCPDEBUG((LOG_INFO, + "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n", + ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag)); + break; + + default: + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting unknown CI type %d\n", citype)); + orc = CONFREJ; + break; + } + endswitch: - if (orc == CONFACK && /* Good CI */ - rc != CONFACK) /* but prior CI wasnt? */ - continue; /* Don't send this one */ - - if (orc == CONFNAK) { /* Nak this CI? */ - if (reject_if_disagree) { /* Getting fed up with sending NAKs? */ - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting too many naks\n")); - orc = CONFREJ; /* Get tough if so */ - } else { - if (rc == CONFREJ) /* Rejecting prior CI? */ - continue; /* Don't send this one */ - if (rc == CONFACK) { /* Ack'd all prior CIs? */ - rc = CONFNAK; /* Not anymore... */ - ucp = inp; /* Backup */ - } - } - } - - if (orc == CONFREJ && /* Reject this CI */ - rc != CONFREJ) { /* but no prior ones? */ - rc = CONFREJ; - ucp = inp; /* Backup */ - } - - /* Need to move CI? */ - if (ucp != cip) - BCOPY(cip, ucp, cilen); /* Move it */ - - /* Update output pointer */ - INCPTR(cilen, ucp); - } - - /* - * If we aren't rejecting this packet, and we want to negotiate - * their address, and they didn't send their address, then we - * send a NAK with a CI_ADDR option appended. We assume the - * input buffer is long enough that we can append the extra - * option safely. - */ - if (rc != CONFREJ && !ho->neg_addr && - wo->req_addr && !reject_if_disagree) { - IPCPDEBUG((LOG_INFO, "ipcp_reqci: Requesting peer address\n")); - if (rc == CONFACK) { - rc = CONFNAK; - ucp = inp; /* reset pointer */ - wo->req_addr = 0; /* don't ask again */ - } - PUTCHAR(CI_ADDR, ucp); - PUTCHAR(CILEN_ADDR, ucp); - tl = ntohl(wo->hisaddr); - PUTLONG(tl, ucp); - } - - *len = (int)(ucp - inp); /* Compute output length */ - IPCPDEBUG((LOG_INFO, "ipcp_reqci: returning Configure-%s\n", CODENAME(rc))); - return (rc); /* Return final code */ + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) { /* Getting fed up with sending NAKs? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting too many naks\n")); + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) { + BCOPY(cip, ucp, cilen); /* Move it */ + } + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Requesting peer address\n")); + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = (int)(ucp - inp); /* Compute output length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: returning Configure-%s\n", CODENAME(rc))); + return (rc); /* Return final code */ } @@ -1170,18 +1223,19 @@ endswitch: * ip_check_options - check that any IP-related options are OK, * and assign appropriate defaults. */ -static void ip_check_options(u_long localAddr) +static void +ip_check_options(u32_t localAddr) { - ipcp_options *wo = &ipcp_wantoptions[0]; + ipcp_options *wo = &ipcp_wantoptions[0]; - /* - * Load our default IP address but allow the remote host to give us - * a new address. - */ - if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) { - wo->accept_local = 1; /* don't insist on this default value */ - wo->ouraddr = htonl(localAddr); - } + /* + * Load our default IP address but allow the remote host to give us + * a new address. + */ + if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) { + wo->accept_local = 1; /* don't insist on this default value */ + wo->ouraddr = htonl(localAddr); + } } #endif @@ -1191,83 +1245,87 @@ static void ip_check_options(u_long loca * * Configure the IP network interface appropriately and bring it up. */ -static void ipcp_up(fsm *f) +static void +ipcp_up(fsm *f) { - u32_t mask; - ipcp_options *ho = &ipcp_hisoptions[f->unit]; - ipcp_options *go = &ipcp_gotoptions[f->unit]; - ipcp_options *wo = &ipcp_wantoptions[f->unit]; - - np_up(f->unit, PPP_IP); - IPCPDEBUG((LOG_INFO, "ipcp: up\n")); - - /* - * We must have a non-zero IP address for both ends of the link. - */ - if (!ho->neg_addr) - ho->hisaddr = wo->hisaddr; - - if (ho->hisaddr == 0) { - IPCPDEBUG((LOG_ERR, "Could not determine remote IP address\n")); - ipcp_close(f->unit, "Could not determine remote IP address"); - return; - } - if (go->ouraddr == 0) { - IPCPDEBUG((LOG_ERR, "Could not determine local IP address\n")); - ipcp_close(f->unit, "Could not determine local IP address"); - return; - } - - if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { - /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/ - } + u32_t mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG((LOG_INFO, "ipcp: up\n")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) { + ho->hisaddr = wo->hisaddr; + } + + if (ho->hisaddr == 0) { + IPCPDEBUG((LOG_ERR, "Could not determine remote IP address\n")); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + IPCPDEBUG((LOG_ERR, "Could not determine local IP address\n")); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } + + if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/ + } - /* - * Check that the peer is allowed to use the IP address it wants. - */ - if (!auth_ip_addr(f->unit, ho->hisaddr)) { - IPCPDEBUG((LOG_ERR, "Peer is not authorized to use remote address %s\n", - inet_ntoa(ho->hisaddr))); - ipcp_close(f->unit, "Unauthorized remote IP address"); - return; - } - - /* set tcp compression */ - sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); - - /* - * Set IP addresses and (if specified) netmask. - */ - mask = GetMask(go->ouraddr); - - if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) { - IPCPDEBUG((LOG_WARNING, "sifaddr failed\n")); - ipcp_close(f->unit, "Interface configuration failed"); - return; - } - - /* bring the interface up for IP */ - if (!sifup(f->unit)) { - IPCPDEBUG((LOG_WARNING, "sifup failed\n")); - ipcp_close(f->unit, "Interface configuration failed"); - return; - } - - sifnpmode(f->unit, PPP_IP, NPMODE_PASS); - - /* assign a default route through the interface if required */ - if (ipcp_wantoptions[f->unit].default_route) - if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) - default_route_set[f->unit] = 1; - - IPCPDEBUG((LOG_NOTICE, "local IP address %s\n", inet_ntoa(go->ouraddr))); - IPCPDEBUG((LOG_NOTICE, "remote IP address %s\n", inet_ntoa(ho->hisaddr))); - if (go->dnsaddr[0]) { - IPCPDEBUG((LOG_NOTICE, "primary DNS address %s\n", inet_ntoa(go->dnsaddr[0]))); - } - if (go->dnsaddr[1]) { - IPCPDEBUG((LOG_NOTICE, "secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1]))); - } + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + IPCPDEBUG((LOG_ERR, "Peer is not authorized to use remote address %s\n", + inet_ntoa(ho->hisaddr))); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); + + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG((LOG_WARNING, "sifup failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) { + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) { + default_route_set[f->unit] = 1; + } + } + + IPCPDEBUG((LOG_NOTICE, "local IP address %s\n", inet_ntoa(go->ouraddr))); + IPCPDEBUG((LOG_NOTICE, "remote IP address %s\n", inet_ntoa(ho->hisaddr))); + if (go->dnsaddr[0]) { + IPCPDEBUG((LOG_NOTICE, "primary DNS address %s\n", inet_ntoa(go->dnsaddr[0]))); + } + if (go->dnsaddr[1]) { + IPCPDEBUG((LOG_NOTICE, "secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1]))); + } } @@ -1277,55 +1335,54 @@ static void ipcp_up(fsm *f) * Take the IP network interface down, clear its addresses * and delete routes through it. */ -static void ipcp_down(fsm *f) +static void +ipcp_down(fsm *f) { - IPCPDEBUG((LOG_INFO, "ipcp: down\n")); - np_down(f->unit, PPP_IP); - sifvjcomp(f->unit, 0, 0, 0); - - sifdown(f->unit); - ipcp_clear_addrs(f->unit); + IPCPDEBUG((LOG_INFO, "ipcp: down\n")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + sifdown(f->unit); + ipcp_clear_addrs(f->unit); } /* * ipcp_clear_addrs() - clear the interface addresses, routes, etc. */ -static void ipcp_clear_addrs(int unit) +static void +ipcp_clear_addrs(int unit) { - u32_t ouraddr, hisaddr; - - ouraddr = ipcp_gotoptions[unit].ouraddr; - hisaddr = ipcp_hisoptions[unit].hisaddr; - if (default_route_set[unit]) { - cifdefaultroute(unit, ouraddr, hisaddr); - default_route_set[unit] = 0; - } - cifaddr(unit, ouraddr, hisaddr); + u32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); } /* * ipcp_finished - possibly shut down the lower layers. */ -static void ipcp_finished(fsm *f) +static void +ipcp_finished(fsm *f) { - np_finished(f->unit, PPP_IP); + np_finished(f->unit, PPP_IP); } #if 0 -static int ipcp_printpkt( - u_char *p, - int plen, - void (*printer) (void *, char *, ...), - void *arg -) +static int +ipcp_printpkt(u8_t *p, int plen, void (*printer) (void *, char *, ...), void *arg) { - (void)p; - (void)plen; - (void)printer; - (void)arg; - return 0; + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; } /* @@ -1333,44 +1390,50 @@ static int ipcp_printpkt( * We don't bring the link up for IP fragments or for TCP FIN packets * with no data. */ -#define IP_HDRLEN 20 /* bytes */ -#define IP_OFFMASK 0x1fff -#define IPPROTO_TCP 6 -#define TCP_HDRLEN 20 -#define TH_FIN 0x01 +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 /* * We use these macros because the IP header may be at an odd address, * and some compilers might use word loads to get th_off or ip_hl. */ -#define net_short(x) (((x)[0] << 8) + (x)[1]) -#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) -#define get_ipoff(x) net_short((unsigned char *)(x) + 6) -#define get_ipproto(x) (((unsigned char *)(x))[9]) -#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) -#define get_tcpflags(x) (((unsigned char *)(x))[13]) +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) -static int ip_active_pkt(u_char *pkt, int len) +static int +ip_active_pkt(u8_t *pkt, int len) { - u_char *tcp; - int hlen; - - len -= PPP_HDRLEN; - pkt += PPP_HDRLEN; - if (len < IP_HDRLEN) - return 0; - if ((get_ipoff(pkt) & IP_OFFMASK) != 0) - return 0; - if (get_ipproto(pkt) != IPPROTO_TCP) - return 1; - hlen = get_iphl(pkt) * 4; - if (len < hlen + TCP_HDRLEN) - return 0; - tcp = pkt + hlen; - if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) - return 0; - return 1; + u8_t *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) { + return 0; + } + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) { + return 0; + } + if (get_ipproto(pkt) != IPPROTO_TCP) { + return 1; + } + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) { + return 0; + } + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) { + return 0; + } + return 1; } #endif diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.h b/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/ipcp.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-04 Guy Lancaster , Global Election Systems Inc. -* Original derived from BSD codes. +* Original derived from BSD codes. *****************************************************************************/ /* * ipcp.h - IP Control Protocol definitions. @@ -48,7 +48,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: ipcp.h,v 1.2 2006/03/29 10:33:28 asl Exp $ + * $Id: ipcp.h,v 1.3 2010/01/26 11:27:49 jld Exp $ */ #ifndef IPCP_H @@ -60,23 +60,23 @@ /* * Options. */ -#define CI_ADDRS 1 /* IP Addresses */ -#define CI_COMPRESSTYPE 2 /* Compression Type */ -#define CI_ADDR 3 +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 -#define CI_MS_WINS1 128 /* Primary WINS value */ -#define CI_MS_DNS1 129 /* Primary DNS value */ -#define CI_MS_WINS2 130 /* Secondary WINS value */ -#define CI_MS_DNS2 131 /* Secondary DNS value */ +#define CI_MS_WINS1 128 /* Primary WINS value */ +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS2 130 /* Secondary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ -#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ -#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ -#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ - /* maxslot and slot number compression) */ +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ -#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/ -#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ - /* compression option*/ +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option */ /************************ @@ -84,23 +84,23 @@ ************************/ typedef struct ipcp_options { - u_int neg_addr : 1; /* Negotiate IP Address? */ - u_int old_addrs : 1; /* Use old (IP-Addresses) option? */ - u_int req_addr : 1; /* Ask peer to send IP address? */ - u_int default_route : 1; /* Assign default route through interface? */ - u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */ - u_int neg_vj : 1; /* Van Jacobson Compression? */ - u_int old_vj : 1; /* use old (short) form of VJ option? */ - u_int accept_local : 1; /* accept peer's value for ouraddr */ - u_int accept_remote : 1; /* accept peer's value for hisaddr */ - u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */ - u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */ - u_short vj_protocol; /* protocol value to use in VJ option */ - u_char maxslotindex; /* VJ slots - 1. */ - u_char cflag; /* VJ slot compression flag. */ - u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ - u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ - u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ + u32_t neg_addr : 1; /* Negotiate IP Address? */ + u32_t old_addrs : 1; /* Use old (IP-Addresses) option? */ + u32_t req_addr : 1; /* Ask peer to send IP address? */ + u32_t default_route : 1; /* Assign default route through interface? */ + u32_t proxy_arp : 1; /* Make proxy ARP entry for peer? */ + u32_t neg_vj : 1; /* Van Jacobson Compression? */ + u32_t old_vj : 1; /* use old (short) form of VJ option? */ + u32_t accept_local : 1; /* accept peer's value for ouraddr */ + u32_t accept_remote : 1; /* accept peer's value for hisaddr */ + u32_t req_dns1 : 1; /* Ask peer to send primary DNS address? */ + u32_t req_dns2 : 1; /* Ask peer to send secondary DNS address? */ + u16_t vj_protocol; /* protocol value to use in VJ option */ + u8_t maxslotindex; /* VJ slots - 1. */ + u8_t cflag; /* VJ slot compression flag. */ + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ } ipcp_options; @@ -121,6 +121,4 @@ extern struct protent ipcp_protent; *** PUBLIC FUNCTIONS *** ***********************/ - #endif /* IPCP_H */ - diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.c b/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-01 Guy Lancaster , Global Election Systems Inc. -* Original. +* Original. *****************************************************************************/ /* @@ -49,17 +49,29 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ - -#include - -#include "ppp.h" -#if PPP_SUPPORT > 0 + + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" +#include "timesys.h" + #include "fsm.h" #include "chap.h" #include "magic.h" #include "auth.h" #include "lcp.h" -#include "pppdebug.h" + +#include + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#else +#define PPPOE_MAXMTU PPP_MAXMRU +#endif /*************************/ @@ -68,13 +80,13 @@ /* * Length of each type of configuration option (in octets) */ -#define CILEN_VOID 2 -#define CILEN_CHAR 3 -#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ -#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ -#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ -#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ -#define CILEN_CBCP 3 +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 /***********************************/ @@ -83,84 +95,83 @@ /* * Callbacks for fsm code. (CI = Configuration Information) */ -static void lcp_resetci (fsm*); /* Reset our CI */ -static int lcp_cilen (fsm*); /* Return length of our CI */ -static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */ -static int lcp_ackci (fsm*, u_char*, int);/* Peer ack'd our CI */ -static int lcp_nakci (fsm*, u_char*, int);/* Peer nak'd our CI */ -static int lcp_rejci (fsm*, u_char*, int);/* Peer rej'd our CI */ -static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */ -static void lcp_up (fsm*); /* We're UP */ -static void lcp_down (fsm*); /* We're DOWN */ -static void lcp_starting (fsm*); /* We need lower layer up */ -static void lcp_finished (fsm*); /* We need lower layer down */ -static int lcp_extcode (fsm*, int, u_char, u_char*, int); +static void lcp_resetci (fsm*); /* Reset our CI */ +static int lcp_cilen (fsm*); /* Return length of our CI */ +static void lcp_addci (fsm*, u8_t*, int*); /* Add our CI to pkt */ +static int lcp_ackci (fsm*, u8_t*, int); /* Peer ack'd our CI */ +static int lcp_nakci (fsm*, u8_t*, int); /* Peer nak'd our CI */ +static int lcp_rejci (fsm*, u8_t*, int); /* Peer rej'd our CI */ +static int lcp_reqci (fsm*, u8_t*, int*, int); /* Rcv peer CI */ +static void lcp_up (fsm*); /* We're UP */ +static void lcp_down (fsm*); /* We're DOWN */ +static void lcp_starting (fsm*); /* We need lower layer up */ +static void lcp_finished (fsm*); /* We need lower layer down */ +static int lcp_extcode (fsm*, int, u8_t, u8_t*, int); -static void lcp_rprotrej (fsm*, u_char*, int); +static void lcp_rprotrej (fsm*, u8_t*, int); /* * routines to send LCP echos to peer */ static void lcp_echo_lowerup (int); static void lcp_echo_lowerdown (int); -static void LcpEchoTimeout (void*); -static void lcp_received_echo_reply (fsm*, int, u_char*, int); -static void LcpSendEchoRequest (fsm*); -static void LcpLinkFailure (fsm*); -static void LcpEchoCheck (fsm*); +static void lcp_echo_timeout (void*); +static void lcp_received_echo_reply (fsm*, int, u8_t*, int); +static void lcp_send_echo_request (fsm*); +static void lcp_link_failure (fsm*); +static void lcp_echo_check (fsm*); /* * Protocol entry points. * Some of these are called directly. */ -static void lcp_input (int, u_char *, int); +static void lcp_input (int, u8_t *, int); static void lcp_protrej (int); -#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ - (x) == CONFNAK ? "NAK" : "REJ") +#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ") /******************************/ /*** PUBLIC DATA STRUCTURES ***/ /******************************/ /* global vars */ -LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ -lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ -lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ -lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ -lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ -ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */ +LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */ /*****************************/ /*** LOCAL DATA STRUCTURES ***/ /*****************************/ -static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ -static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ -static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ -static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ -static u32_t lcp_echo_number = 0; /* ID number of next echo frame */ -static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ +static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ +static u32_t lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u32_t lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ +static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ -static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ +static u8_t nak_buffer[PPP_MRU]; /* where we construct a nak packet */ -static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ - lcp_resetci, /* Reset our Configuration Information */ - lcp_cilen, /* Length of our Configuration Information */ - lcp_addci, /* Add our Configuration Information */ - lcp_ackci, /* ACK our Configuration Information */ - lcp_nakci, /* NAK our Configuration Information */ - lcp_rejci, /* Reject our Configuration Information */ - lcp_reqci, /* Request peer's Configuration Information */ - lcp_up, /* Called when fsm reaches OPENED state */ - lcp_down, /* Called when fsm leaves OPENED state */ - lcp_starting, /* Called when we want the lower layer up */ - lcp_finished, /* Called when we want the lower layer down */ - NULL, /* Called when Protocol-Reject received */ - NULL, /* Retransmission is necessary */ - lcp_extcode, /* Called to handle LCP-specific codes */ - "LCP" /* String name of protocol */ +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches LS_OPENED state */ + lcp_down, /* Called when fsm leaves LS_OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ }; struct protent lcp_protent = { @@ -195,163 +206,169 @@ int lcp_loopbackfail = DEFLOOPBACKFAIL; /* * lcp_init - Initialize LCP. */ -void lcp_init(int unit) +void +lcp_init(int unit) { - fsm *f = &lcp_fsm[unit]; - lcp_options *wo = &lcp_wantoptions[unit]; - lcp_options *ao = &lcp_allowoptions[unit]; - - f->unit = unit; - f->protocol = PPP_LCP; - f->callbacks = &lcp_callbacks; - - fsm_init(f); - - wo->passive = 0; - wo->silent = 0; - wo->restart = 0; /* Set to 1 in kernels or multi-line - * implementations */ - wo->neg_mru = 1; - wo->mru = PPP_DEFMRU; - wo->neg_asyncmap = 1; - wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ - wo->neg_chap = 0; /* Set to 1 on server */ - wo->neg_upap = 0; /* Set to 1 on server */ - wo->chap_mdtype = CHAP_DIGEST_MD5; - wo->neg_magicnumber = 1; - wo->neg_pcompression = 1; - wo->neg_accompression = 1; - wo->neg_lqr = 0; /* no LQR implementation yet */ - wo->neg_cbcp = 0; - - ao->neg_mru = 1; - ao->mru = PPP_MAXMRU; - ao->neg_asyncmap = 1; - ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ - ao->neg_chap = (CHAP_SUPPORT != 0); - ao->chap_mdtype = CHAP_DIGEST_MD5; - ao->neg_upap = (PAP_SUPPORT != 0); - ao->neg_magicnumber = 1; - ao->neg_pcompression = 1; - ao->neg_accompression = 1; - ao->neg_lqr = 0; /* no LQR implementation yet */ - ao->neg_cbcp = (CBCP_SUPPORT != 0); + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */ + wo->neg_mru = 1; + wo->mru = PPP_DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; - /* - * Set transmit escape for the flag and escape characters plus anything - * set for the allowable options. - */ - memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); - xmit_accm[unit][15] = 0x60; - xmit_accm[unit][0] = (u_char)(ao->asyncmap & 0xFF); - xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF); - xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF); - xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF); - LCPDEBUG((LOG_INFO, "lcp_init: xmit_accm=%X %X %X %X\n", - xmit_accm[unit][0], - xmit_accm[unit][1], - xmit_accm[unit][2], - xmit_accm[unit][3])); - - lcp_phase[unit] = PHASE_INITIALIZE; + ao->neg_mru = 1; + ao->mru = PPP_MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + ao->neg_chap = (CHAP_SUPPORT != 0); + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = (PAP_SUPPORT != 0); + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ + ao->neg_cbcp = (CBCP_SUPPORT != 0); + + /* + * Set transmit escape for the flag and escape characters plus anything + * set for the allowable options. + */ + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][15] = 0x60; + xmit_accm[unit][0] = (u8_t)((ao->asyncmap & 0xFF)); + xmit_accm[unit][1] = (u8_t)((ao->asyncmap >> 8) & 0xFF); + xmit_accm[unit][2] = (u8_t)((ao->asyncmap >> 16) & 0xFF); + xmit_accm[unit][3] = (u8_t)((ao->asyncmap >> 24) & 0xFF); + LCPDEBUG((LOG_INFO, "lcp_init: xmit_accm=%X %X %X %X\n", + xmit_accm[unit][0], + xmit_accm[unit][1], + xmit_accm[unit][2], + xmit_accm[unit][3])); + + lcp_phase[unit] = PHASE_INITIALIZE; } /* * lcp_open - LCP is allowed to come up. */ -void lcp_open(int unit) +void +lcp_open(int unit) { - fsm *f = &lcp_fsm[unit]; - lcp_options *wo = &lcp_wantoptions[unit]; - - f->flags = 0; - if (wo->passive) - f->flags |= OPT_PASSIVE; - if (wo->silent) - f->flags |= OPT_SILENT; - fsm_open(f); - - lcp_phase[unit] = PHASE_ESTABLISH; + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) { + f->flags |= OPT_PASSIVE; + } + if (wo->silent) { + f->flags |= OPT_SILENT; + } + fsm_open(f); + + lcp_phase[unit] = PHASE_ESTABLISH; } /* * lcp_close - Take LCP down. */ -void lcp_close(int unit, char *reason) +void +lcp_close(int unit, char *reason) { - fsm *f = &lcp_fsm[unit]; - - if (lcp_phase[unit] != PHASE_DEAD) - lcp_phase[unit] = PHASE_TERMINATE; - if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { - /* - * This action is not strictly according to the FSM in RFC1548, - * but it does mean that the program terminates if you do an - * lcp_close() in passive/silent mode when a connection hasn't - * been established. - */ - f->state = CLOSED; - lcp_finished(f); - } - else - fsm_close(&lcp_fsm[unit], reason); + fsm *f = &lcp_fsm[unit]; + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do an + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = LS_CLOSED; + lcp_finished(f); + } else { + fsm_close(&lcp_fsm[unit], reason); + } } /* * lcp_lowerup - The lower layer is up. */ -void lcp_lowerup(int unit) +void +lcp_lowerup(int unit) { - lcp_options *wo = &lcp_wantoptions[unit]; - - /* - * Don't use A/C or protocol compression on transmission, - * but accept A/C and protocol compressed packets - * if we are going to ask for A/C and protocol compression. - */ - ppp_set_xaccm(unit, &xmit_accm[unit]); - ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0); - ppp_recv_config(unit, PPP_MRU, 0x00000000l, - wo->neg_pcompression, wo->neg_accompression); - peer_mru[unit] = PPP_MRU; - lcp_allowoptions[unit].asyncmap - = (u_long)xmit_accm[unit][0] - | ((u_long)xmit_accm[unit][1] << 8) - | ((u_long)xmit_accm[unit][2] << 16) - | ((u_long)xmit_accm[unit][3] << 24); - LCPDEBUG((LOG_INFO, "lcp_lowerup: asyncmap=%X %X %X %X\n", - xmit_accm[unit][3], - xmit_accm[unit][2], - xmit_accm[unit][1], - xmit_accm[unit][0])); - - fsm_lowerup(&lcp_fsm[unit]); + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, &xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0x00000000l, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = (u32_t)xmit_accm[unit][0] + | ((u32_t)xmit_accm[unit][1] << 8) + | ((u32_t)xmit_accm[unit][2] << 16) + | ((u32_t)xmit_accm[unit][3] << 24); + LCPDEBUG((LOG_INFO, "lcp_lowerup: asyncmap=%X %X %X %X\n", + xmit_accm[unit][3], + xmit_accm[unit][2], + xmit_accm[unit][1], + xmit_accm[unit][0])); + + fsm_lowerup(&lcp_fsm[unit]); } /* * lcp_lowerdown - The lower layer is down. */ -void lcp_lowerdown(int unit) +void +lcp_lowerdown(int unit) { - fsm_lowerdown(&lcp_fsm[unit]); + fsm_lowerdown(&lcp_fsm[unit]); } /* * lcp_sprotrej - Send a Protocol-Reject for some protocol. */ -void lcp_sprotrej(int unit, u_char *p, int len) +void +lcp_sprotrej(int unit, u8_t *p, int len) { - /* - * Send back the protocol and the information field of the - * rejected packet. We only get here if LCP is in the OPENED state. - */ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the LS_OPENED state. + */ - fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, - p, len); + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len); } @@ -362,121 +379,122 @@ void lcp_sprotrej(int unit, u_char *p, i /* * lcp_input - Input LCP packet. */ -static void lcp_input(int unit, u_char *p, int len) +static void +lcp_input(int unit, u8_t *p, int len) { - fsm *f = &lcp_fsm[unit]; - - fsm_input(f, p, len); + fsm *f = &lcp_fsm[unit]; + + fsm_input(f, p, len); } /* * lcp_extcode - Handle a LCP-specific code. */ -static int lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len) +static int +lcp_extcode(fsm *f, int code, u8_t id, u8_t *inp, int len) { - u_char *magp; - - switch( code ){ - case PROTREJ: - lcp_rprotrej(f, inp, len); - break; - - case ECHOREQ: - if (f->state != OPENED) - break; - LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d\n", id)); - magp = inp; - PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); - fsm_sdata(f, ECHOREP, id, inp, len); - break; - - case ECHOREP: - lcp_received_echo_reply(f, id, inp, len); - break; - - case DISCREQ: - break; - - default: - return 0; - } - return 1; + u8_t *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != LS_OPENED) { + break; + } + LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d\n", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; } - + /* * lcp_rprotrej - Receive an Protocol-Reject. * * Figure out which protocol is rejected and inform it. */ -static void lcp_rprotrej(fsm *f, u_char *inp, int len) +static void +lcp_rprotrej(fsm *f, u8_t *inp, int len) { - int i; - struct protent *protp; - u_short prot; - - if (len < sizeof (u_short)) { - LCPDEBUG((LOG_INFO, - "lcp_rprotrej: Rcvd short Protocol-Reject packet!\n")); - return; - } - - GETSHORT(prot, inp); - - LCPDEBUG((LOG_INFO, - "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", - prot)); - - /* - * Protocol-Reject packets received in any state other than the LCP - * OPENED state SHOULD be silently discarded. - */ - if( f->state != OPENED ){ - LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d\n", - f->state)); - return; - } - - /* - * Upcall the proper Protocol-Reject routine. - */ - for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) - if (protp->protocol == prot && protp->enabled_flag) { - (*protp->protrej)(f->unit); - return; - } - - LCPDEBUG((LOG_WARNING, "Protocol-Reject for unsupported protocol 0x%x\n", - prot)); + int i; + struct protent *protp; + u16_t prot; + + if (len < sizeof (u16_t)) { + LCPDEBUG((LOG_INFO, "lcp_rprotrej: Rcvd short Protocol-Reject packet!\n")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG((LOG_INFO, "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * LS_OPENED state SHOULD be silently discarded. + */ + if( f->state != LS_OPENED ) { + LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d\n", f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + } + + LCPDEBUG((LOG_WARNING, "Protocol-Reject for unsupported protocol 0x%x\n", prot)); } /* * lcp_protrej - A Protocol-Reject was received. */ -static void lcp_protrej(int unit) +static void +lcp_protrej(int unit) { - (void)unit; - /* - * Can't reject LCP! - */ - LCPDEBUG((LOG_WARNING, - "lcp_protrej: Received Protocol-Reject for LCP!\n")); - fsm_protreject(&lcp_fsm[unit]); + LWIP_UNUSED_ARG(unit); + /* + * Can't reject LCP! + */ + LCPDEBUG((LOG_WARNING, "lcp_protrej: Received Protocol-Reject for LCP!\n")); + fsm_protreject(&lcp_fsm[unit]); } /* * lcp_resetci - Reset our CI. */ -static void lcp_resetci(fsm *f) +static void +lcp_resetci(fsm *f) { - lcp_wantoptions[f->unit].magicnumber = magic(); - lcp_wantoptions[f->unit].numloops = 0; - lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; - peer_mru[f->unit] = PPP_MRU; - auth_reset(f->unit); + lcp_wantoptions[f->unit].magicnumber = magic_next(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); } @@ -485,97 +503,97 @@ static void lcp_resetci(fsm *f) */ static int lcp_cilen(fsm *f) { - lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; -#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) -#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) -#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) -#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) -#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) -#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) - /* - * NB: we only ask for one of CHAP and UPAP, even if we will - * accept either. - */ - return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + - LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + - LENCICHAP(go->neg_chap) + - LENCISHORT(!go->neg_chap && go->neg_upap) + - LENCILQR(go->neg_lqr) + - LENCICBCP(go->neg_cbcp) + - LENCILONG(go->neg_magicnumber) + - LENCIVOID(go->neg_pcompression) + - LENCIVOID(go->neg_accompression)); +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); } /* * lcp_addci - Add our desired CIs to a packet. */ -static void lcp_addci(fsm *f, u_char *ucp, int *lenp) +static void +lcp_addci(fsm *f, u8_t *ucp, int *lenp) { - lcp_options *go = &lcp_gotoptions[f->unit]; - u_char *start_ucp = ucp; - + lcp_options *go = &lcp_gotoptions[f->unit]; + u8_t *start_ucp = ucp; + #define ADDCIVOID(opt, neg) \ - if (neg) { \ - LCPDEBUG((LOG_INFO, "lcp_addci: opt=%d\n", opt)); \ - PUTCHAR(opt, ucp); \ - PUTCHAR(CILEN_VOID, ucp); \ - } + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: opt=%d\n", opt)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } #define ADDCISHORT(opt, neg, val) \ - if (neg) { \ - LCPDEBUG((LOG_INFO, "lcp_addci: INT opt=%d %X\n", opt, val)); \ - PUTCHAR(opt, ucp); \ - PUTCHAR(CILEN_SHORT, ucp); \ - PUTSHORT(val, ucp); \ - } + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: INT opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } #define ADDCICHAP(opt, neg, val, digest) \ - if (neg) { \ - LCPDEBUG((LOG_INFO, "lcp_addci: CHAP opt=%d %X\n", opt, val)); \ - PUTCHAR(opt, ucp); \ - PUTCHAR(CILEN_CHAP, ucp); \ - PUTSHORT(val, ucp); \ - PUTCHAR(digest, ucp); \ - } + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: CHAP opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } #define ADDCILONG(opt, neg, val) \ - if (neg) { \ - LCPDEBUG((LOG_INFO, "lcp_addci: L opt=%d %lX\n", opt, val)); \ - PUTCHAR(opt, ucp); \ - PUTCHAR(CILEN_LONG, ucp); \ - PUTLONG(val, ucp); \ - } + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: L opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } #define ADDCILQR(opt, neg, val) \ - if (neg) { \ - LCPDEBUG((LOG_INFO, "lcp_addci: LQR opt=%d %lX\n", opt, val)); \ - PUTCHAR(opt, ucp); \ - PUTCHAR(CILEN_LQR, ucp); \ - PUTSHORT(PPP_LQR, ucp); \ - PUTLONG(val, ucp); \ - } + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: LQR opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } #define ADDCICHAR(opt, neg, val) \ - if (neg) { \ - LCPDEBUG((LOG_INFO, "lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \ - PUTCHAR(opt, ucp); \ - PUTCHAR(CILEN_CHAR, ucp); \ - PUTCHAR(val, ucp); \ - } - - ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); - ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, - go->asyncmap); - ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); - ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); - ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); - ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); - ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); - ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); - ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); - - if (ucp - start_ucp != *lenp) { - /* this should never happen, because peer_mtu should be 1500 */ - LCPDEBUG((LOG_ERR, "Bug in lcp_addci: wrong length\n")); - } + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + LCPDEBUG((LOG_ERR, "Bug in lcp_addci: wrong length\n")); + } } @@ -584,566 +602,585 @@ static void lcp_addci(fsm *f, u_char *uc * This should not modify any state if the Ack is bad. * * Returns: - * 0 - Ack was bad. - * 1 - Ack was good. + * 0 - Ack was bad. + * 1 - Ack was good. */ -static int lcp_ackci(fsm *f, u_char *p, int len) +static int +lcp_ackci(fsm *f, u8_t *p, int len) { - lcp_options *go = &lcp_gotoptions[f->unit]; - u_char cilen, citype, cichar; - u_short cishort; - u32_t cilong; - - /* - * CIs must be in exactly the same order that we sent. - * Check packet length and CI length at each step. - * If we find any deviations, then this packet is bad. - */ + lcp_options *go = &lcp_gotoptions[f->unit]; + u8_t cilen, citype, cichar; + u16_t cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ #define ACKCIVOID(opt, neg) \ - if (neg) { \ - if ((len -= CILEN_VOID) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != CILEN_VOID || \ - citype != opt) \ - goto bad; \ - } + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || citype != opt) \ + goto bad; \ + } #define ACKCISHORT(opt, neg, val) \ - if (neg) { \ - if ((len -= CILEN_SHORT) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != CILEN_SHORT || \ - citype != opt) \ - goto bad; \ - GETSHORT(cishort, p); \ - if (cishort != val) \ - goto bad; \ - } + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } #define ACKCICHAR(opt, neg, val) \ - if (neg) { \ - if ((len -= CILEN_CHAR) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != CILEN_CHAR || \ - citype != opt) \ - goto bad; \ - GETCHAR(cichar, p); \ - if (cichar != val) \ - goto bad; \ - } + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } #define ACKCICHAP(opt, neg, val, digest) \ - if (neg) { \ - if ((len -= CILEN_CHAP) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != CILEN_CHAP || \ - citype != opt) \ - goto bad; \ - GETSHORT(cishort, p); \ - if (cishort != val) \ - goto bad; \ - GETCHAR(cichar, p); \ - if (cichar != digest) \ - goto bad; \ - } + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } #define ACKCILONG(opt, neg, val) \ - if (neg) { \ - if ((len -= CILEN_LONG) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != CILEN_LONG || \ - citype != opt) \ - goto bad; \ - GETLONG(cilong, p); \ - if (cilong != val) \ - goto bad; \ - } + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } #define ACKCILQR(opt, neg, val) \ - if (neg) { \ - if ((len -= CILEN_LQR) < 0) \ - goto bad; \ - GETCHAR(citype, p); \ - GETCHAR(cilen, p); \ - if (cilen != CILEN_LQR || \ - citype != opt) \ - goto bad; \ - GETSHORT(cishort, p); \ - if (cishort != PPP_LQR) \ - goto bad; \ - GETLONG(cilong, p); \ - if (cilong != val) \ - goto bad; \ - } - - ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); - ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, - go->asyncmap); - ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); - ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); - ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); - ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); - ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); - ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); - ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); - - /* - * If there are any remaining CIs, then this packet is bad. - */ - if (len != 0) - goto bad; - LCPDEBUG((LOG_INFO, "lcp_acki: Ack\n")); - return (1); + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + LCPDEBUG((LOG_INFO, "lcp_acki: Ack\n")); + return (1); bad: - LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!\n")); - return (0); + LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!\n")); + return (0); } /* * lcp_nakci - Peer has sent a NAK for some of our CIs. * This should not modify any state if the Nak is bad - * or if LCP is in the OPENED state. + * or if LCP is in the LS_OPENED state. * * Returns: - * 0 - Nak was bad. - * 1 - Nak was good. + * 0 - Nak was bad. + * 1 - Nak was good. */ -static int lcp_nakci(fsm *f, u_char *p, int len) +static int +lcp_nakci(fsm *f, u8_t *p, int len) { - lcp_options *go = &lcp_gotoptions[f->unit]; - lcp_options *wo = &lcp_wantoptions[f->unit]; - u_char citype, cichar, *next; - u_short cishort; - u32_t cilong; - lcp_options no; /* options we've seen Naks for */ - lcp_options try; /* options to request next time */ - int looped_back = 0; - int cilen; - - BZERO(&no, sizeof(no)); - try = *go; - - /* - * Any Nak'd CIs must be in exactly the same order that we sent. - * Check packet length and CI length at each step. - * If we find any deviations, then this packet is bad. - */ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u8_t citype, cichar, *next; + u16_t cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ #define NAKCIVOID(opt, neg, code) \ - if (go->neg && \ - len >= CILEN_VOID && \ - p[1] == CILEN_VOID && \ - p[0] == opt) { \ - len -= CILEN_VOID; \ - INCPTR(CILEN_VOID, p); \ - no.neg = 1; \ - code \ - } + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } #define NAKCICHAP(opt, neg, code) \ - if (go->neg && \ - len >= CILEN_CHAP && \ - p[1] == CILEN_CHAP && \ - p[0] == opt) { \ - len -= CILEN_CHAP; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - GETCHAR(cichar, p); \ - no.neg = 1; \ - code \ - } + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } #define NAKCICHAR(opt, neg, code) \ - if (go->neg && \ - len >= CILEN_CHAR && \ - p[1] == CILEN_CHAR && \ - p[0] == opt) { \ - len -= CILEN_CHAR; \ - INCPTR(2, p); \ - GETCHAR(cichar, p); \ - no.neg = 1; \ - code \ - } + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } #define NAKCISHORT(opt, neg, code) \ - if (go->neg && \ - len >= CILEN_SHORT && \ - p[1] == CILEN_SHORT && \ - p[0] == opt) { \ - len -= CILEN_SHORT; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - no.neg = 1; \ - code \ - } + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } #define NAKCILONG(opt, neg, code) \ - if (go->neg && \ - len >= CILEN_LONG && \ - p[1] == CILEN_LONG && \ - p[0] == opt) { \ - len -= CILEN_LONG; \ - INCPTR(2, p); \ - GETLONG(cilong, p); \ - no.neg = 1; \ - code \ - } + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } #define NAKCILQR(opt, neg, code) \ - if (go->neg && \ - len >= CILEN_LQR && \ - p[1] == CILEN_LQR && \ - p[0] == opt) { \ - len -= CILEN_LQR; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - GETLONG(cilong, p); \ - no.neg = 1; \ - code \ - } - - /* - * We don't care if they want to send us smaller packets than - * we want. Therefore, accept any MRU less than what we asked for, - * but then ignore the new value when setting the MRU in the kernel. - * If they send us a bigger MRU than what we asked, accept it, up to - * the limit of the default MRU we'd get if we didn't negotiate. - */ - if (go->neg_mru && go->mru != PPP_DEFMRU) { - NAKCISHORT(CI_MRU, neg_mru, - if (cishort <= wo->mru || cishort < PPP_DEFMRU) - try.mru = cishort; - ); - } - - /* - * Add any characters they want to our (receive-side) asyncmap. - */ - if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) { - NAKCILONG(CI_ASYNCMAP, neg_asyncmap, - try.asyncmap = go->asyncmap | cilong; - ); - } - - /* - * If they've nak'd our authentication-protocol, check whether - * they are proposing a different protocol, or a different - * hash algorithm for CHAP. - */ - if ((go->neg_chap || go->neg_upap) - && len >= CILEN_SHORT - && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { - cilen = p[1]; - len -= cilen; - no.neg_chap = go->neg_chap; - no.neg_upap = go->neg_upap; - INCPTR(2, p); - GETSHORT(cishort, p); - if (cishort == PPP_PAP && cilen == CILEN_SHORT) { - /* - * If we were asking for CHAP, they obviously don't want to do it. - * If we weren't asking for CHAP, then we were asking for PAP, - * in which case this Nak is bad. - */ - if (!go->neg_chap) - goto bad; - try.neg_chap = 0; - - } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { - GETCHAR(cichar, p); - if (go->neg_chap) { - /* - * We were asking for CHAP/MD5; they must want a different - * algorithm. If they can't do MD5, we'll have to stop - * asking for CHAP. - */ - if (cichar != go->chap_mdtype) - try.neg_chap = 0; - } else { - /* - * Stop asking for PAP if we were asking for it. - */ - try.neg_upap = 0; - } - - } else { - /* - * We don't recognize what they're suggesting. - * Stop asking for what we were asking for. - */ - if (go->neg_chap) - try.neg_chap = 0; - else - try.neg_upap = 0; - p += cilen - CILEN_SHORT; - } - } - - /* - * If they can't cope with our link quality protocol, we'll have - * to stop asking for LQR. We haven't got any other protocol. - * If they Nak the reporting period, take their value XXX ? - */ - NAKCILQR(CI_QUALITY, neg_lqr, - if (cishort != PPP_LQR) - try.neg_lqr = 0; - else - try.lqr_period = cilong; - ); - - /* - * Only implementing CBCP...not the rest of the callback options - */ - NAKCICHAR(CI_CALLBACK, neg_cbcp, - try.neg_cbcp = 0; - ); - - /* - * Check for a looped-back line. - */ - NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, - try.magicnumber = magic(); - looped_back = 1; - ); - - /* - * Peer shouldn't send Nak for protocol compression or - * address/control compression requests; they should send - * a Reject instead. If they send a Nak, treat it as a Reject. - */ - NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, - try.neg_pcompression = 0; - ); - NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, - try.neg_accompression = 0; - ); - - /* - * There may be remaining CIs, if the peer is requesting negotiation - * on an option that we didn't include in our request packet. - * If we see an option that we requested, or one we've already seen - * in this packet, then this packet is bad. - * If we wanted to respond by starting to negotiate on the requested - * option(s), we could, but we don't, because except for the - * authentication type and quality protocol, if we are not negotiating - * an option, it is because we were told not to. - * For the authentication type, the Nak from the peer means - * `let me authenticate myself with you' which is a bit pointless. - * For the quality protocol, the Nak means `ask me to send you quality - * reports', but if we didn't ask for them, we don't want them. - * An option we don't recognize represents the peer asking to - * negotiate some option we don't support, so ignore it. - */ - while (len > CILEN_VOID) { - GETCHAR(citype, p); - GETCHAR(cilen, p); - if (cilen < CILEN_VOID || (len -= cilen) < 0) - goto bad; - next = p + cilen - 2; - - switch (citype) { - case CI_MRU: - if ((go->neg_mru && go->mru != PPP_DEFMRU) - || no.neg_mru || cilen != CILEN_SHORT) - goto bad; - GETSHORT(cishort, p); - if (cishort < PPP_DEFMRU) - try.mru = cishort; - break; - case CI_ASYNCMAP: - if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) - || no.neg_asyncmap || cilen != CILEN_LONG) - goto bad; - break; - case CI_AUTHTYPE: - if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) - goto bad; - break; - case CI_MAGICNUMBER: - if (go->neg_magicnumber || no.neg_magicnumber || - cilen != CILEN_LONG) - goto bad; - break; - case CI_PCOMPRESSION: - if (go->neg_pcompression || no.neg_pcompression - || cilen != CILEN_VOID) - goto bad; - break; - case CI_ACCOMPRESSION: - if (go->neg_accompression || no.neg_accompression - || cilen != CILEN_VOID) - goto bad; - break; - case CI_QUALITY: - if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) - goto bad; - break; - } - p = next; - } - - /* If there is still anything left, this packet is bad. */ - if (len != 0) - goto bad; - - /* - * OK, the Nak is good. Now we can update state. - */ - if (f->state != OPENED) { - if (looped_back) { - if (++try.numloops >= lcp_loopbackfail) { - LCPDEBUG((LOG_NOTICE, "Serial line is looped back.\n")); - lcp_close(f->unit, "Loopback detected"); - } - } - else - try.numloops = 0; - *go = try; - } - - return 1; - + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != PPP_DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort < PPP_DEFMRU) { + try.mru = cishort; + } + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) { + goto bad; + } + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) { + try.neg_chap = 0; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) { + try.neg_chap = 0; + } else { + try.neg_upap = 0; + } + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) { + try.neg_lqr = 0; + } else { + try.lqr_period = cilong; + } + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic_next(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != PPP_DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) { + goto bad; + } + GETSHORT(cishort, p); + if (cishort < PPP_DEFMRU) { + try.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + || no.neg_asyncmap || cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) { + goto bad; + } + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) { + goto bad; + } + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + LCPDEBUG((LOG_NOTICE, "Serial line is looped back.\n")); + lcp_close(f->unit, "Loopback detected"); + } + } else { + try.numloops = 0; + } + *go = try; + } + + return 1; + bad: - LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!\n")); - return 0; + LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!\n")); + return 0; } /* * lcp_rejci - Peer has Rejected some of our CIs. * This should not modify any state if the Reject is bad - * or if LCP is in the OPENED state. + * or if LCP is in the LS_OPENED state. * * Returns: - * 0 - Reject was bad. - * 1 - Reject was good. + * 0 - Reject was bad. + * 1 - Reject was good. */ -static int lcp_rejci(fsm *f, u_char *p, int len) +static int +lcp_rejci(fsm *f, u8_t *p, int len) { - lcp_options *go = &lcp_gotoptions[f->unit]; - u_char cichar; - u_short cishort; - u32_t cilong; - lcp_options try; /* options to request next time */ - - try = *go; - - /* - * Any Rejected CIs must be in exactly the same order that we sent. - * Check packet length and CI length at each step. - * If we find any deviations, then this packet is bad. - */ + lcp_options *go = &lcp_gotoptions[f->unit]; + u8_t cichar; + u16_t cishort; + u32_t cilong; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ #define REJCIVOID(opt, neg) \ - if (go->neg && \ - len >= CILEN_VOID && \ - p[1] == CILEN_VOID && \ - p[0] == opt) { \ - len -= CILEN_VOID; \ - INCPTR(CILEN_VOID, p); \ - try.neg = 0; \ - LCPDEBUG((LOG_INFO, "lcp_rejci: void opt %d rejected\n", opt)); \ - } + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO, "lcp_rejci: void opt %d rejected\n", opt)); \ + } #define REJCISHORT(opt, neg, val) \ - if (go->neg && \ - len >= CILEN_SHORT && \ - p[1] == CILEN_SHORT && \ - p[0] == opt) { \ - len -= CILEN_SHORT; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - /* Check rejected value. */ \ - if (cishort != val) \ - goto bad; \ - try.neg = 0; \ - LCPDEBUG((LOG_INFO,"lcp_rejci: short opt %d rejected\n", opt)); \ - } + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: short opt %d rejected\n", opt)); \ + } #define REJCICHAP(opt, neg, val, digest) \ - if (go->neg && \ - len >= CILEN_CHAP && \ - p[1] == CILEN_CHAP && \ - p[0] == opt) { \ - len -= CILEN_CHAP; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - GETCHAR(cichar, p); \ - /* Check rejected value. */ \ - if (cishort != val || cichar != digest) \ - goto bad; \ - try.neg = 0; \ - try.neg_upap = 0; \ - LCPDEBUG((LOG_INFO,"lcp_rejci: chap opt %d rejected\n", opt)); \ - } + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) { \ + goto bad; \ + } \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: chap opt %d rejected\n", opt)); \ + } #define REJCILONG(opt, neg, val) \ - if (go->neg && \ - len >= CILEN_LONG && \ - p[1] == CILEN_LONG && \ - p[0] == opt) { \ - len -= CILEN_LONG; \ - INCPTR(2, p); \ - GETLONG(cilong, p); \ - /* Check rejected value. */ \ - if (cilong != val) \ - goto bad; \ - try.neg = 0; \ - LCPDEBUG((LOG_INFO,"lcp_rejci: long opt %d rejected\n", opt)); \ - } + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: long opt %d rejected\n", opt)); \ + } #define REJCILQR(opt, neg, val) \ - if (go->neg && \ - len >= CILEN_LQR && \ - p[1] == CILEN_LQR && \ - p[0] == opt) { \ - len -= CILEN_LQR; \ - INCPTR(2, p); \ - GETSHORT(cishort, p); \ - GETLONG(cilong, p); \ - /* Check rejected value. */ \ - if (cishort != PPP_LQR || cilong != val) \ - goto bad; \ - try.neg = 0; \ - LCPDEBUG((LOG_INFO,"lcp_rejci: LQR opt %d rejected\n", opt)); \ - } + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: LQR opt %d rejected\n", opt)); \ + } #define REJCICBCP(opt, neg, val) \ - if (go->neg && \ - len >= CILEN_CBCP && \ - p[1] == CILEN_CBCP && \ - p[0] == opt) { \ - len -= CILEN_CBCP; \ - INCPTR(2, p); \ - GETCHAR(cichar, p); \ - /* Check rejected value. */ \ - if (cichar != val) \ - goto bad; \ - try.neg = 0; \ - LCPDEBUG((LOG_INFO,"lcp_rejci: Callback opt %d rejected\n", opt)); \ - } - - REJCISHORT(CI_MRU, neg_mru, go->mru); - REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); - REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); - if (!go->neg_chap) { - REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); - } - REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); - REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); - REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); - REJCIVOID(CI_PCOMPRESSION, neg_pcompression); - REJCIVOID(CI_ACCOMPRESSION, neg_accompression); - - /* - * If there are any remaining CIs, then this packet is bad. - */ - if (len != 0) - goto bad; - /* - * Now we can update state. - */ - if (f->state != OPENED) - *go = try; - return 1; - + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: Callback opt %d rejected\n", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + bad: - LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!\n")); - return 0; + LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!\n")); + return 0; } @@ -1154,456 +1191,462 @@ bad: * appropriately. If reject_if_disagree is non-zero, doesn't return * CONFNAK; returns CONFREJ if it can't return CONFACK. */ -static int lcp_reqci(fsm *f, - u_char *inp, /* Requested CIs */ - int *lenp, /* Length of requested CIs */ - int reject_if_disagree) +static int +lcp_reqci(fsm *f, + u8_t *inp, /* Requested CIs */ + int *lenp, /* Length of requested CIs */ + int reject_if_disagree) { - lcp_options *go = &lcp_gotoptions[f->unit]; - lcp_options *ho = &lcp_hisoptions[f->unit]; - lcp_options *ao = &lcp_allowoptions[f->unit]; - u_char *cip, *next; /* Pointer to current and next CIs */ - int cilen, citype, cichar; /* Parsed len, type, char value */ - u_short cishort; /* Parsed short value */ - u32_t cilong; /* Parse long value */ - int rc = CONFACK; /* Final packet return code */ - int orc; /* Individual option return code */ - u_char *p; /* Pointer to next char to parse */ - u_char *rejp; /* Pointer to next char in reject frame */ - u_char *nakp; /* Pointer to next char in Nak frame */ - int l = *lenp; /* Length left */ -#if TRACELCP > 0 - char traceBuf[80]; - int traceNdx = 0; -#endif - - /* - * Reset all his options. - */ - BZERO(ho, sizeof(*ho)); - - /* - * Process all his options. - */ - next = inp; - nakp = nak_buffer; - rejp = inp; - while (l) { - orc = CONFACK; /* Assume success */ - cip = p = next; /* Remember begining of CI */ - if (l < 2 || /* Not enough data for CI header or */ - p[1] < 2 || /* CI length too small or */ - p[1] > l) { /* CI length too big? */ - LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!\n")); - orc = CONFREJ; /* Reject bad CI */ - cilen = l; /* Reject till end of packet */ - l = 0; /* Don't loop again */ - citype = 0; - goto endswitch; - } - GETCHAR(citype, p); /* Parse CI type */ - GETCHAR(cilen, p); /* Parse CI length */ - l -= cilen; /* Adjust remaining length */ - next += cilen; /* Step to next CI */ - - switch (citype) { /* Check CI type */ - case CI_MRU: - if (!ao->neg_mru) { /* Allow option? */ - LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - not allowed\n")); - orc = CONFREJ; /* Reject CI */ - break; - } else if (cilen != CILEN_SHORT) { /* Check CI length */ - LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - bad length\n")); - orc = CONFREJ; /* Reject CI */ - break; - } - GETSHORT(cishort, p); /* Parse MRU */ - - /* - * He must be able to receive at least our minimum. - * No need to check a maximum. If he sends a large number, - * we'll just ignore it. - */ - if (cishort < PPP_MINMRU) { - LCPDEBUG((LOG_INFO, "lcp_reqci: Nak - MRU too small\n")); - orc = CONFNAK; /* Nak CI */ - PUTCHAR(CI_MRU, nakp); - PUTCHAR(CILEN_SHORT, nakp); - PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */ - break; - } - ho->neg_mru = 1; /* Remember he sent MRU */ - ho->mru = cishort; /* And remember value */ -#if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort); - traceNdx = strlen(traceBuf); -#endif - break; - - case CI_ASYNCMAP: - if (!ao->neg_asyncmap) { - LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP not allowed\n")); - orc = CONFREJ; - break; - } else if (cilen != CILEN_LONG) { - LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP bad length\n")); - orc = CONFREJ; - break; - } - GETLONG(cilong, p); - - /* - * Asyncmap must have set at least the bits - * which are set in lcp_allowoptions[unit].asyncmap. - */ - if ((ao->asyncmap & ~cilong) != 0) { - LCPDEBUG((LOG_INFO, "lcp_reqci: Nak ASYNCMAP %lX missing %lX\n", - cilong, ao->asyncmap)); - orc = CONFNAK; - PUTCHAR(CI_ASYNCMAP, nakp); - PUTCHAR(CILEN_LONG, nakp); - PUTLONG(ao->asyncmap | cilong, nakp); - break; - } - ho->neg_asyncmap = 1; - ho->asyncmap = cilong; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u8_t *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype, cichar; /* Parsed len, type, char value */ + u16_t cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u8_t *p; /* Pointer to next char to parse */ + u8_t *rejp; /* Pointer to next char in reject frame */ + u8_t *nakp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ #if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong); - traceNdx = strlen(traceBuf); -#endif - break; - - case CI_AUTHTYPE: - if (cilen < CILEN_SHORT) { - LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE missing arg\n")); - orc = CONFREJ; - break; - } else if (!(ao->neg_upap || ao->neg_chap)) { - /* - * Reject the option if we're not willing to authenticate. - */ - LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE not allowed\n")); - orc = CONFREJ; - break; - } - GETSHORT(cishort, p); - - /* - * Authtype must be UPAP or CHAP. - * - * Note: if both ao->neg_upap and ao->neg_chap are set, - * and the peer sends a Configure-Request with two - * authenticate-protocol requests, one for CHAP and one - * for UPAP, then we will reject the second request. - * Whether we end up doing CHAP or UPAP depends then on - * the ordering of the CIs in the peer's Configure-Request. - */ - - if (cishort == PPP_PAP) { - if (ho->neg_chap) { /* we've already accepted CHAP */ - LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP already accepted\n")); - orc = CONFREJ; - break; - } else if (cilen != CILEN_SHORT) { - LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP bad len\n")); - orc = CONFREJ; - break; - } - if (!ao->neg_upap) { /* we don't want to do PAP */ - LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE PAP not allowed\n")); - orc = CONFNAK; /* NAK it and suggest CHAP */ - PUTCHAR(CI_AUTHTYPE, nakp); - PUTCHAR(CILEN_CHAP, nakp); - PUTSHORT(PPP_CHAP, nakp); - PUTCHAR(ao->chap_mdtype, nakp); - break; - } - ho->neg_upap = 1; -#if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort); - traceNdx = strlen(traceBuf); -#endif - break; - } - if (cishort == PPP_CHAP) { - if (ho->neg_upap) { /* we've already accepted PAP */ - LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n")); - orc = CONFREJ; - break; - } else if (cilen != CILEN_CHAP) { - LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP bad len\n")); - orc = CONFREJ; - break; - } - if (!ao->neg_chap) { /* we don't want to do CHAP */ - LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP not allowed\n")); - orc = CONFNAK; /* NAK it and suggest PAP */ - PUTCHAR(CI_AUTHTYPE, nakp); - PUTCHAR(CILEN_SHORT, nakp); - PUTSHORT(PPP_PAP, nakp); - break; - } - GETCHAR(cichar, p); /* get digest type*/ - if (cichar != CHAP_DIGEST_MD5 -#ifdef CHAPMS - && cichar != CHAP_MICROSOFT -#endif - ) { - LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", cichar)); - orc = CONFNAK; - PUTCHAR(CI_AUTHTYPE, nakp); - PUTCHAR(CILEN_CHAP, nakp); - PUTSHORT(PPP_CHAP, nakp); - PUTCHAR(ao->chap_mdtype, nakp); - break; - } -#if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, cichar); - traceNdx = strlen(traceBuf); -#endif - ho->chap_mdtype = cichar; /* save md type */ - ho->neg_chap = 1; - break; - } - - /* - * We don't recognize the protocol they're asking for. - * Nak it with something we're willing to do. - * (At this point we know ao->neg_upap || ao->neg_chap.) - */ - orc = CONFNAK; - PUTCHAR(CI_AUTHTYPE, nakp); - if (ao->neg_chap) { - LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort)); - PUTCHAR(CILEN_CHAP, nakp); - PUTSHORT(PPP_CHAP, nakp); - PUTCHAR(ao->chap_mdtype, nakp); - } - else { - LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort)); - PUTCHAR(CILEN_SHORT, nakp); - PUTSHORT(PPP_PAP, nakp); - } - break; - - case CI_QUALITY: - GETSHORT(cishort, p); - GETLONG(cilong, p); -#if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong); - traceNdx = strlen(traceBuf); + char traceBuf[80]; + int traceNdx = 0; #endif - if (!ao->neg_lqr || - cilen != CILEN_LQR) { - orc = CONFREJ; - break; - } - - /* - * Check the protocol and the reporting period. - * XXX When should we Nak this, and what with? - */ - if (cishort != PPP_LQR) { - orc = CONFNAK; - PUTCHAR(CI_QUALITY, nakp); - PUTCHAR(CILEN_LQR, nakp); - PUTSHORT(PPP_LQR, nakp); - PUTLONG(ao->lqr_period, nakp); - break; - } - break; - - case CI_MAGICNUMBER: - if (!(ao->neg_magicnumber || go->neg_magicnumber) || - cilen != CILEN_LONG) { - orc = CONFREJ; - break; - } - GETLONG(cilong, p); + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = nak_buffer; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru) { /* Allow option? */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_SHORT) { /* Check CI length */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - bad length\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < PPP_MINMRU) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Nak - MRU too small\n")); + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_LONG) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP bad length\n")); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Nak ASYNCMAP %lX missing %lX\n", + cilong, ao->asyncmap)); + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(ao->asyncmap | cilong, nakp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; #if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong); - traceNdx = strlen(traceBuf); + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE missing arg\n")); + orc = CONFREJ; + break; + } else if (!(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE not allowed\n")); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap) { /* we've already accepted CHAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP already accepted\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_SHORT) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE PAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap) { /* we've already accepted PAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_CHAP) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#ifdef CHAPMS + && cichar != CHAP_MICROSOFT +#endif + ) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", cichar)); + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, cichar); + traceNdx = strlen(traceBuf); +#endif + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort)); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort)); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + GETSHORT(cishort, p); + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong); + traceNdx = strlen(traceBuf); #endif - /* - * He must have a different magic number. - */ - if (go->neg_magicnumber && - cilong == go->magicnumber) { - cilong = magic(); /* Don't put magic() inside macro! */ - orc = CONFNAK; - PUTCHAR(CI_MAGICNUMBER, nakp); - PUTCHAR(CILEN_LONG, nakp); - PUTLONG(cilong, nakp); - break; - } - ho->neg_magicnumber = 1; - ho->magicnumber = cilong; - break; - - - case CI_PCOMPRESSION: -#if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION"); - traceNdx = strlen(traceBuf); -#endif - if (!ao->neg_pcompression || - cilen != CILEN_VOID) { - orc = CONFREJ; - break; - } - ho->neg_pcompression = 1; - break; - - case CI_ACCOMPRESSION: + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakp); + PUTCHAR(CILEN_LQR, nakp); + PUTSHORT(PPP_LQR, nakp); + PUTLONG(ao->lqr_period, nakp); + break; + } + break; + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); #if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION"); - traceNdx = strlen(traceBuf); + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong); + traceNdx = strlen(traceBuf); #endif - if (!ao->neg_accompression || - cilen != CILEN_VOID) { - orc = CONFREJ; - break; - } - ho->neg_accompression = 1; - break; - - case CI_MRRU: + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic_next(); + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(cilong, nakp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: #if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU"); - traceNdx = strlen(traceBuf); + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION"); + traceNdx = strlen(traceBuf); #endif - orc = CONFREJ; - break; - - case CI_SSNHF: + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: #if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF"); - traceNdx = strlen(traceBuf); + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION"); + traceNdx = strlen(traceBuf); #endif - orc = CONFREJ; - break; - - case CI_EPDISC: + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + case CI_MRRU: #if TRACELCP > 0 - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC"); - traceNdx = strlen(traceBuf); + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU"); + traceNdx = strlen(traceBuf); #endif - orc = CONFREJ; - break; - - default: -#if TRACELCP - snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype); - traceNdx = strlen(traceBuf); + orc = CONFREJ; + break; + + case CI_SSNHF: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_EPDISC: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC"); + traceNdx = strlen(traceBuf); #endif - orc = CONFREJ; - break; - } - - endswitch: + orc = CONFREJ; + break; + + default: #if TRACELCP - if (traceNdx >= 80 - 32) { - LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd%s\n", traceBuf)); - traceNdx = 0; - } + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + } + + endswitch: +#if TRACELCP + if (traceNdx >= 80 - 32) { + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd%s\n", traceBuf)); + traceNdx = 0; + } #endif - if (orc == CONFACK && /* Good CI */ - rc != CONFACK) /* but prior CI wasnt? */ - continue; /* Don't send this one */ - - if (orc == CONFNAK) { /* Nak this CI? */ - if (reject_if_disagree /* Getting fed up with sending NAKs? */ - && citype != CI_MAGICNUMBER) { - orc = CONFREJ; /* Get tough if so */ - } - else { - if (rc == CONFREJ) /* Rejecting prior CI? */ - continue; /* Don't send this one */ - rc = CONFNAK; - } - } - if (orc == CONFREJ) { /* Reject this CI */ - rc = CONFREJ; - if (cip != rejp) /* Need to move rejected CI? */ - BCOPY(cip, rejp, cilen); /* Move it */ - INCPTR(cilen, rejp); /* Update output pointer */ - } - } - - /* - * If we wanted to send additional NAKs (for unsent CIs), the - * code would go here. The extra NAKs would go at *nakp. - * At present there are no cases where we want to ask the - * peer to negotiate an option. - */ - - switch (rc) { - case CONFACK: - *lenp = (int)(next - inp); - break; - case CONFNAK: - /* - * Copy the Nak'd options from the nak_buffer to the caller's buffer. - */ - *lenp = (int)(nakp - nak_buffer); - BCOPY(nak_buffer, inp, *lenp); - break; - case CONFREJ: - *lenp = (int)(rejp - inp); - break; - } - + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) { /* Need to move rejected CI? */ + BCOPY(cip, rejp, cilen); /* Move it */ + } + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = (int)(next - inp); + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak_buffer to the caller's buffer. + */ + *lenp = (int)(nakp - nak_buffer); + BCOPY(nak_buffer, inp, *lenp); + break; + case CONFREJ: + *lenp = (int)(rejp - inp); + break; + } + #if TRACELCP > 0 - if (traceNdx > 0) { - LCPDEBUG((LOG_INFO, "lcp_reqci: %s\n", traceBuf)); - } + if (traceNdx > 0) { + LCPDEBUG((LOG_INFO, "lcp_reqci: %s\n", traceBuf)); + } #endif - LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.\n", CODENAME(rc))); - return (rc); /* Return final code */ + LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.\n", CODENAME(rc))); + return (rc); /* Return final code */ } /* * lcp_up - LCP has come UP. */ -static void lcp_up(fsm *f) +static void +lcp_up(fsm *f) { - lcp_options *wo = &lcp_wantoptions[f->unit]; - lcp_options *ho = &lcp_hisoptions[f->unit]; - lcp_options *go = &lcp_gotoptions[f->unit]; - lcp_options *ao = &lcp_allowoptions[f->unit]; - - if (!go->neg_magicnumber) - go->magicnumber = 0; - if (!ho->neg_magicnumber) - ho->magicnumber = 0; - - /* - * Set our MTU to the smaller of the MTU we wanted and - * the MRU our peer wanted. If we negotiated an MRU, - * set our MRU to the larger of value we wanted and - * the value we got in the negotiation. - */ - ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), - (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl), - ho->neg_pcompression, ho->neg_accompression); - /* - * If the asyncmap hasn't been negotiated, we really should - * set the receive asyncmap to ffffffff, but we set it to 0 - * for backwards contemptibility. - */ - ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU), - (go->neg_asyncmap? go->asyncmap: 0x00000000), - go->neg_pcompression, go->neg_accompression); - - if (ho->neg_mru) - peer_mru[f->unit] = ho->mru; - - lcp_echo_lowerup(f->unit); /* Enable echo messages */ - - link_established(f->unit); + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + if (!go->neg_magicnumber) { + go->magicnumber = 0; + } + if (!ho->neg_magicnumber) { + ho->magicnumber = 0; + } + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl), + ho->neg_pcompression, ho->neg_accompression); + /* + * If the asyncmap hasn't been negotiated, we really should + * set the receive asyncmap to ffffffff, but we set it to 0 + * for backwards contemptibility. + */ + ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU), + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) { + peer_mru[f->unit] = ho->mru; + } + + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); } @@ -1612,37 +1655,40 @@ static void lcp_up(fsm *f) * * Alert other protocols. */ -static void lcp_down(fsm *f) +static void +lcp_down(fsm *f) { - lcp_options *go = &lcp_gotoptions[f->unit]; - - lcp_echo_lowerdown(f->unit); - - link_down(f->unit); - - ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0); - ppp_recv_config(f->unit, PPP_MRU, - (go->neg_asyncmap? go->asyncmap: 0x00000000), - go->neg_pcompression, go->neg_accompression); - peer_mru[f->unit] = PPP_MRU; + lcp_options *go = &lcp_gotoptions[f->unit]; + + lcp_echo_lowerdown(f->unit); + + link_down(f->unit); + + ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(f->unit, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + peer_mru[f->unit] = PPP_MRU; } /* * lcp_starting - LCP needs the lower layer up. */ -static void lcp_starting(fsm *f) +static void +lcp_starting(fsm *f) { - link_required(f->unit); + link_required(f->unit); } /* * lcp_finished - LCP has finished with the lower layer. */ -static void lcp_finished(fsm *f) +static void +lcp_finished(fsm *f) { - link_terminated(f->unit); + link_terminated(f->unit); } @@ -1651,39 +1697,36 @@ static void lcp_finished(fsm *f) * print_string - print a readable representation of a string using * printer. */ -static void print_string( - char *p, - int len, - void (*printer) (void *, char *, ...), - void *arg -) +static void +print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg) { - int c; - - printer(arg, "\""); - for (; len > 0; --len) { - c = *p++; - if (' ' <= c && c <= '~') { - if (c == '\\' || c == '"') - printer(arg, "\\"); - printer(arg, "%c", c); - } else { - switch (c) { - case '\n': - printer(arg, "\\n"); - break; - case '\r': - printer(arg, "\\r"); - break; - case '\t': - printer(arg, "\\t"); - break; - default: - printer(arg, "\\%.3o", c); - } + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') { + printer(arg, "\\"); + } + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); } } - printer(arg, "\""); + } + printer(arg, "\""); } @@ -1691,301 +1734,303 @@ static void print_string( * lcp_printpkt - print the contents of an LCP packet. */ static char *lcp_codenames[] = { - "ConfReq", "ConfAck", "ConfNak", "ConfRej", - "TermReq", "TermAck", "CodeRej", "ProtRej", - "EchoReq", "EchoRep", "DiscReq" + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" }; -static int lcp_printpkt( - u_char *p, - int plen, - void (*printer) (void *, char *, ...), - void *arg -) +static int +lcp_printpkt( u8_t *p, int plen, void (*printer) (void *, char *, ...), void *arg) { - int code, id, len, olen; - u_char *pstart, *optend; - u_short cishort; - u32_t cilong; - - if (plen < HEADERLEN) - return 0; - pstart = p; - GETCHAR(code, p); - GETCHAR(id, p); - GETSHORT(len, p); - if (len < HEADERLEN || len > plen) - return 0; - - if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) - printer(arg, " %s", lcp_codenames[code-1]); - else - printer(arg, " code=0x%x", code); - printer(arg, " id=0x%x", id); - len -= HEADERLEN; - switch (code) { - case CONFREQ: - case CONFACK: - case CONFNAK: - case CONFREJ: - /* print option list */ - while (len >= 2) { - GETCHAR(code, p); - GETCHAR(olen, p); - p -= 2; - if (olen < 2 || olen > len) { - break; - } - printer(arg, " <"); - len -= olen; - optend = p + olen; - switch (code) { - case CI_MRU: - if (olen == CILEN_SHORT) { - p += 2; - GETSHORT(cishort, p); - printer(arg, "mru %d", cishort); - } - break; - case CI_ASYNCMAP: - if (olen == CILEN_LONG) { - p += 2; - GETLONG(cilong, p); - printer(arg, "asyncmap 0x%lx", cilong); - } - break; - case CI_AUTHTYPE: - if (olen >= CILEN_SHORT) { - p += 2; - printer(arg, "auth "); - GETSHORT(cishort, p); - switch (cishort) { - case PPP_PAP: - printer(arg, "pap"); - break; - case PPP_CHAP: - printer(arg, "chap"); - break; - default: - printer(arg, "0x%x", cishort); - } - } - break; - case CI_QUALITY: - if (olen >= CILEN_SHORT) { - p += 2; - printer(arg, "quality "); - GETSHORT(cishort, p); - switch (cishort) { - case PPP_LQR: - printer(arg, "lqr"); - break; - default: - printer(arg, "0x%x", cishort); - } - } - break; - case CI_CALLBACK: - if (olen >= CILEN_CHAR) { - p += 2; - printer(arg, "callback "); - GETSHORT(cishort, p); - switch (cishort) { - case CBCP_OPT: - printer(arg, "CBCP"); - break; - default: - printer(arg, "0x%x", cishort); - } - } - break; - case CI_MAGICNUMBER: - if (olen == CILEN_LONG) { - p += 2; - GETLONG(cilong, p); - printer(arg, "magic 0x%x", cilong); - } - break; - case CI_PCOMPRESSION: - if (olen == CILEN_VOID) { - p += 2; - printer(arg, "pcomp"); - } - break; - case CI_ACCOMPRESSION: - if (olen == CILEN_VOID) { - p += 2; - printer(arg, "accomp"); - } - break; - } - while (p < optend) { - GETCHAR(code, p); - printer(arg, " %.2x", code); - } - printer(arg, ">"); - } - break; - - case TERMACK: - case TERMREQ: - if (len > 0 && *p >= ' ' && *p < 0x7f) { - printer(arg, " "); - print_string((char*)p, len, printer, arg); - p += len; - len = 0; - } - break; - - case ECHOREQ: - case ECHOREP: - case DISCREQ: - if (len >= 4) { - GETLONG(cilong, p); - printer(arg, " magic=0x%x", cilong); - p += 4; - len -= 4; - } - break; - } - - /* print the rest of the bytes in the packet */ - for (; len > 0; --len) { - GETCHAR(code, p); - printer(arg, " %.2x", code); - } - - return (int)(p - pstart); + int code, id, len, olen; + u8_t *pstart, *optend; + u16_t cishort; + u32_t cilong; + + if (plen < HEADERLEN) { + return 0; + } + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) { + printer(arg, " %s", lcp_codenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%lx", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_PAP: + printer(arg, "pap"); + break; + case PPP_CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETSHORT(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string((char*)p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return (int)(p - pstart); } #endif /* * Time to shut down the link because there is nothing out there. */ - -static void LcpLinkFailure (fsm *f) +static void +lcp_link_failure (fsm *f) { - if (f->state == OPENED) { - LCPDEBUG((LOG_INFO, "No response to %d echo-requests\n", lcp_echos_pending)); - LCPDEBUG((LOG_NOTICE, "Serial link appears to be disconnected.\n")); - lcp_close(f->unit, "Peer not responding"); - } + if (f->state == LS_OPENED) { + LCPDEBUG((LOG_INFO, "No response to %d echo-requests\n", lcp_echos_pending)); + LCPDEBUG((LOG_NOTICE, "Serial link appears to be disconnected.\n")); + lcp_close(f->unit, "Peer not responding"); + } } /* * Timer expired for the LCP echo requests from this process. */ - -static void LcpEchoCheck (fsm *f) +static void +lcp_echo_check (fsm *f) { - LcpSendEchoRequest (f); - - /* - * Start the timer for the next interval. - */ - LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0); + lcp_send_echo_request (f); - TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); - lcp_echo_timer_running = 1; + /* + * Start the timer for the next interval. + */ + LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0); + + TIMEOUT (lcp_echo_timeout, f, lcp_echo_interval); + lcp_echo_timer_running = 1; } /* * LcpEchoTimeout - Timer expired on the LCP echo */ - -static void LcpEchoTimeout (void *arg) +static void +lcp_echo_timeout (void *arg) { - if (lcp_echo_timer_running != 0) { - lcp_echo_timer_running = 0; - LcpEchoCheck ((fsm *) arg); - } + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + lcp_echo_check ((fsm *) arg); + } } /* * LcpEchoReply - LCP has received a reply to the echo */ -static void lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len) +static void +lcp_received_echo_reply (fsm *f, int id, u8_t *inp, int len) { - u32_t magic; - - (void)id; + u32_t magic; + + LWIP_UNUSED_ARG(id); - /* Check the magic number - don't count replies from ourselves. */ - if (len < 4) { - LCPDEBUG((LOG_WARNING, "lcp: received short Echo-Reply, length %d\n", len)); - return; - } - GETLONG(magic, inp); - if (lcp_gotoptions[f->unit].neg_magicnumber - && magic == lcp_gotoptions[f->unit].magicnumber) { - LCPDEBUG((LOG_WARNING, "appear to have received our own echo-reply!\n")); - return; - } - - /* Reset the number of outstanding echo frames */ - lcp_echos_pending = 0; + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + LCPDEBUG((LOG_WARNING, "lcp: received short Echo-Reply, length %d\n", len)); + return; + } + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) { + LCPDEBUG((LOG_WARNING, "appear to have received our own echo-reply!\n")); + return; + } + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; } /* * LcpSendEchoRequest - Send an echo request frame to the peer */ - -static void LcpSendEchoRequest (fsm *f) +static void +lcp_send_echo_request (fsm *f) { - u32_t lcp_magic; - u_char pkt[4], *pktp; - - /* - * Detect the failure of the peer at this point. - */ - if (lcp_echo_fails != 0) { - if (lcp_echos_pending++ >= lcp_echo_fails) { - LcpLinkFailure(f); - lcp_echos_pending = 0; - } - } - - /* - * Make and send the echo request frame. - */ - if (f->state == OPENED) { - lcp_magic = lcp_gotoptions[f->unit].magicnumber; - pktp = pkt; - PUTLONG(lcp_magic, pktp); - fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt)); - } + u32_t lcp_magic; + u8_t pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending++ >= lcp_echo_fails) { + lcp_link_failure(f); + lcp_echos_pending = 0; + } + } + + /* + * Make and send the echo request frame. + */ + if (f->state == LS_OPENED) { + lcp_magic = lcp_gotoptions[f->unit].magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, (u8_t)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt)); + } } /* * lcp_echo_lowerup - Start the timer for the LCP frame */ -static void lcp_echo_lowerup (int unit) +static void +lcp_echo_lowerup (int unit) { - fsm *f = &lcp_fsm[unit]; - - /* Clear the parameters for generating echo frames */ - lcp_echos_pending = 0; - lcp_echo_number = 0; - lcp_echo_timer_running = 0; - - /* If a timeout interval is specified then start the timer */ - if (lcp_echo_interval != 0) - LcpEchoCheck (f); + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) { + lcp_echo_check (f); + } } /* * lcp_echo_lowerdown - Stop the timer for the LCP frame */ -static void lcp_echo_lowerdown (int unit) +static void +lcp_echo_lowerdown (int unit) { - fsm *f = &lcp_fsm[unit]; - - if (lcp_echo_timer_running != 0) { - UNTIMEOUT (LcpEchoTimeout, f); - lcp_echo_timer_running = 0; - } + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (lcp_echo_timeout, f); + lcp_echo_timer_running = 0; + } } #endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.h b/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/lcp.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-11-05 Guy Lancaster , Global Election Systems Inc. -* Original derived from BSD codes. +* Original derived from BSD codes. *****************************************************************************/ /* * lcp.h - Link Control Protocol definitions. @@ -48,39 +48,38 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: lcp.h,v 1.2 2006/03/29 10:33:28 asl Exp $ + * $Id: lcp.h,v 1.3 2010/01/26 11:27:49 jld Exp $ */ #ifndef LCP_H #define LCP_H - /************************* *** PUBLIC DEFINITIONS *** *************************/ /* * Options. */ -#define CI_MRU 1 /* Maximum Receive Unit */ -#define CI_ASYNCMAP 2 /* Async Control Character Map */ -#define CI_AUTHTYPE 3 /* Authentication Type */ -#define CI_QUALITY 4 /* Quality Protocol */ -#define CI_MAGICNUMBER 5 /* Magic Number */ -#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ -#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ -#define CI_CALLBACK 13 /* callback */ -#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ -#define CI_SSNHF 18 /* short sequence numbers for multilink */ -#define CI_EPDISC 19 /* endpoint discriminator */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ /* * LCP-specific packet types. */ -#define PROTREJ 8 /* Protocol Reject */ -#define ECHOREQ 9 /* Echo Request */ -#define ECHOREP 10 /* Echo Reply */ -#define DISCREQ 11 /* Discard Request */ -#define CBCP_OPT 6 /* Use callback control protocol */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ /************************ @@ -91,34 +90,34 @@ * The state of options is described by an lcp_options structure. */ typedef struct lcp_options { - u_int passive : 1; /* Don't die if we don't get a response */ - u_int silent : 1; /* Wait for the other end to start first */ - u_int restart : 1; /* Restart vs. exit after close */ - u_int neg_mru : 1; /* Negotiate the MRU? */ - u_int neg_asyncmap : 1; /* Negotiate the async map? */ - u_int neg_upap : 1; /* Ask for UPAP authentication? */ - u_int neg_chap : 1; /* Ask for CHAP authentication? */ - u_int neg_magicnumber : 1; /* Ask for magic number? */ - u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ - u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ - u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ - u_int neg_cbcp : 1; /* Negotiate use of CBCP */ + u32_t passive : 1; /* Don't die if we don't get a response */ + u32_t silent : 1; /* Wait for the other end to start first */ + u32_t restart : 1; /* Restart vs. exit after close */ + u32_t neg_mru : 1; /* Negotiate the MRU? */ + u32_t neg_asyncmap : 1; /* Negotiate the async map? */ + u32_t neg_upap : 1; /* Ask for UPAP authentication? */ + u32_t neg_chap : 1; /* Ask for CHAP authentication? */ + u32_t neg_magicnumber : 1; /* Ask for magic number? */ + u32_t neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + u32_t neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + u32_t neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + u32_t neg_cbcp : 1; /* Negotiate use of CBCP */ #ifdef PPP_MULTILINK - u_int neg_mrru : 1; /* Negotiate multilink MRRU */ - u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */ - u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */ + u32_t neg_mrru : 1; /* Negotiate multilink MRRU */ + u32_t neg_ssnhf : 1; /* Negotiate short sequence numbers */ + u32_t neg_endpoint : 1; /* Negotiate endpoint discriminator */ #endif - u_short mru; /* Value of MRU */ + u16_t mru; /* Value of MRU */ #ifdef PPP_MULTILINK - u_short mrru; /* Value of MRRU, and multilink enable */ + u16_t mrru; /* Value of MRRU, and multilink enable */ #endif - u_char chap_mdtype; /* which MD type (hashing algorithm) */ - u32_t asyncmap; /* Value of async map */ - u32_t magicnumber; - int numloops; /* Number of loops during magic number neg. */ - u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ + u8_t chap_mdtype; /* which MD type (hashing algorithm) */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ #ifdef PPP_MULTILINK - struct epdisc endpoint; /* endpoint discriminator */ + struct epdisc endpoint; /* endpoint discriminator */ #endif } lcp_options; @@ -126,13 +125,13 @@ typedef struct lcp_options { * Values for phase from BSD pppd.h based on RFC 1661. */ typedef enum { - PHASE_DEAD = 0, - PHASE_INITIALIZE, - PHASE_ESTABLISH, - PHASE_AUTHENTICATE, - PHASE_CALLBACK, - PHASE_NETWORK, - PHASE_TERMINATE + PHASE_DEAD = 0, + PHASE_INITIALIZE, + PHASE_ESTABLISH, + PHASE_AUTHENTICATE, + PHASE_CALLBACK, + PHASE_NETWORK, + PHASE_TERMINATE } LinkPhase; @@ -140,7 +139,7 @@ typedef enum { *** PUBLIC DATA STRUCTURES *** *****************************/ -extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ extern lcp_options lcp_wantoptions[]; extern lcp_options lcp_gotoptions[]; extern lcp_options lcp_allowoptions[]; @@ -152,18 +151,17 @@ extern ext_accm xmit_accm[]; *** PUBLIC FUNCTIONS *** ***********************/ -void lcp_init (int); -void lcp_open (int); -void lcp_close (int, char *); -void lcp_lowerup (int); -void lcp_lowerdown (int); -void lcp_sprotrej (int, u_char *, int); /* send protocol reject */ +void lcp_init (int); +void lcp_open (int); +void lcp_close (int, char *); +void lcp_lowerup (int); +void lcp_lowerdown(int); +void lcp_sprotrej (int, u8_t *, int); /* send protocol reject */ extern struct protent lcp_protent; /* Default number of times we receive our magic number from the peer before deciding the link is looped-back. */ -#define DEFLOOPBACKFAIL 10 +#define DEFLOOPBACKFAIL 10 #endif /* LCP_H */ - diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/magic.c b/packages/net/lwip_tcpip/current/src/netif/ppp/magic.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/magic.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/magic.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-04 Guy Lancaster , Global Election Systems Inc. -* Original based on BSD magic.c. +* Original based on BSD magic.c. *****************************************************************************/ /* * magic.c - PPP Magic Number routines. @@ -49,11 +49,14 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#include "ppp.h" +#include "lwip/opt.h" + +#if PPP_SUPPORT + +#include "netif/ppp/ppp.h" #include "randm.h" #include "magic.h" - /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ /***********************************/ @@ -63,17 +66,17 @@ * Since we use another random number generator that has its own * initialization, we do nothing here. */ -void magicInit() +void magic_init() { - return; + return; } /* * magic - Returns the next magic number. */ -u32_t magic() +u32_t magic_next() { - return avRandom(); + return randm_rand(); } - +#endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/magic.h b/packages/net/lwip_tcpip/current/src/netif/ppp/magic.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/magic.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/magic.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -48,7 +48,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: magic.h,v 1.2 2006/03/29 10:33:28 asl Exp $ + * $Id: magic.h,v 1.3 2010/01/26 11:27:49 jld Exp $ */ #ifndef MAGIC_H @@ -58,7 +58,10 @@ ************************** PUBLIC FUNCTIONS ********************************** *****************************************************************************/ -void magicInit(void); /* Initialize the magic number generator */ -u32_t magic(void); /* Returns the next magic number */ +/* Initialize the magic number generator */ +void magic_init(void); + +/* Returns the next magic number */ +u32_t magic_next(void); #endif /* MAGIC_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/md5.c b/packages/net/lwip_tcpip/current/src/netif/ppp/md5.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/md5.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/md5.c @@ -31,25 +31,30 @@ *********************************************************************** */ -#include "ppp.h" -#include "md5.h" +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT || MD5_SUPPORT + +#include "netif/ppp/ppp.h" #include "pppdebug.h" -#if CHAP_SUPPORT > 0 || MD5_SUPPORT > 0 +#include "md5.h" /* *********************************************************************** ** Message-digest routines: ** ** To form the message digest for a message M ** - ** (1) Initialize a context buffer mdContext using MD5Init ** - ** (2) Call MD5Update on mdContext and M ** - ** (3) Call MD5Final on mdContext ** - ** The message digest is now in mdContext->digest[0...15] ** + ** (1) Initialize a context buffer ctx using md5_init ** + ** (2) Call md5_update on ctx and M ** + ** (3) Call md5_final on ctx ** + ** The message digest is now in ctx->digest[0...15] ** *********************************************************************** */ /* forward declaration */ -static void Transform (u32_t *buf, u32_t *in); +static void transform(u32_t *buf, u32_t *in); static unsigned char PADDING[64] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -95,116 +100,122 @@ static unsigned char PADDING[64] = { } #ifdef __STDC__ -#define UL(x) x##UL +#define UL(x) x##UL #else #ifdef WIN32 -#define UL(x) x##UL +#define UL(x) x##UL #else -#define UL(x) x +#define UL(x) x #endif #endif -/* The routine MD5Init initializes the message-digest context +/* The routine md5_init initializes the message-digest context mdContext. All fields are set to zero. */ -void MD5Init (MD5_CTX *mdContext) +void +md5_init(md5_ctx *ctx) { - mdContext->i[0] = mdContext->i[1] = (u32_t)0; + ctx->i[0] = ctx->i[1] = (u32_t)0; - /* Load magic initialization constants. - */ - mdContext->buf[0] = (u32_t)0x67452301UL; - mdContext->buf[1] = (u32_t)0xefcdab89UL; - mdContext->buf[2] = (u32_t)0x98badcfeUL; - mdContext->buf[3] = (u32_t)0x10325476UL; + /* Load magic initialization constants. */ + ctx->buf[0] = (u32_t)0x67452301UL; + ctx->buf[1] = (u32_t)0xefcdab89UL; + ctx->buf[2] = (u32_t)0x98badcfeUL; + ctx->buf[3] = (u32_t)0x10325476UL; } -/* The routine MD5Update updates the message-digest context to - account for the presence of each of the characters inBuf[0..inLen-1] +/* The routine md5_update updates the message-digest context to + account for the presence of each of the characters buf[0..inLen-1] in the message whose digest is being computed. */ -void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +void +md5_update(md5_ctx *ctx, unsigned char *buf, unsigned int len) { u32_t in[16]; int mdi; unsigned int i, ii; #if 0 - ppp_trace(LOG_INFO, "MD5Update: %u:%.*H\n", inLen, MIN(inLen, 20) * 2, inBuf); - ppp_trace(LOG_INFO, "MD5Update: %u:%s\n", inLen, inBuf); + ppp_trace(LOG_INFO, "MD5Update: %u:%.*H\n", len, MIN(len, 20) * 2, buf); + ppp_trace(LOG_INFO, "MD5Update: %u:%s\n", len, buf); #endif - + /* compute number of bytes mod 64 */ - mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + mdi = (int)((ctx->i[0] >> 3) & 0x3F); /* update number of bits */ - if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) - mdContext->i[1]++; - mdContext->i[0] += ((u32_t)inLen << 3); - mdContext->i[1] += ((u32_t)inLen >> 29); + if ((ctx->i[0] + ((u32_t)len << 3)) < ctx->i[0]) { + ctx->i[1]++; + } + ctx->i[0] += ((u32_t)len << 3); + ctx->i[1] += ((u32_t)len >> 29); - while (inLen--) { + while (len--) { /* add new character to buffer, increment mdi */ - mdContext->in[mdi++] = *inBuf++; + ctx->in[mdi++] = *buf++; /* transform if necessary */ if (mdi == 0x40) { - for (i = 0, ii = 0; i < 16; i++, ii += 4) - in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | - (((u32_t)mdContext->in[ii+2]) << 16) | - (((u32_t)mdContext->in[ii+1]) << 8) | - ((u32_t)mdContext->in[ii]); - Transform (mdContext->buf, in); + for (i = 0, ii = 0; i < 16; i++, ii += 4) { + in[i] = (((u32_t)ctx->in[ii+3]) << 24) | + (((u32_t)ctx->in[ii+2]) << 16) | + (((u32_t)ctx->in[ii+1]) << 8) | + ((u32_t)ctx->in[ii]); + } + transform (ctx->buf, in); mdi = 0; } } } -/* The routine MD5Final terminates the message-digest computation and - ends with the desired message digest in mdContext->digest[0...15]. +/* The routine md5_final terminates the message-digest computation and + ends with the desired message digest in ctx->digest[0...15]. */ -void MD5Final (unsigned char hash[], MD5_CTX *mdContext) +void +md5_final(unsigned char hash[], md5_ctx *ctx) { u32_t in[16]; int mdi; unsigned int i, ii; - unsigned int padLen; + unsigned int pad_len; /* save number of bits */ - in[14] = mdContext->i[0]; - in[15] = mdContext->i[1]; + in[14] = ctx->i[0]; + in[15] = ctx->i[1]; /* compute number of bytes mod 64 */ - mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + mdi = (int)((ctx->i[0] >> 3) & 0x3F); /* pad out to 56 mod 64 */ - padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); - MD5Update (mdContext, PADDING, padLen); + pad_len = (mdi < 56) ? (56 - mdi) : (120 - mdi); + md5_update (ctx, PADDING, pad_len); /* append length in bits and transform */ - for (i = 0, ii = 0; i < 14; i++, ii += 4) - in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | - (((u32_t)mdContext->in[ii+2]) << 16) | - (((u32_t)mdContext->in[ii+1]) << 8) | - ((u32_t)mdContext->in[ii]); - Transform (mdContext->buf, in); + for (i = 0, ii = 0; i < 14; i++, ii += 4) { + in[i] = (((u32_t)ctx->in[ii+3]) << 24) | + (((u32_t)ctx->in[ii+2]) << 16) | + (((u32_t)ctx->in[ii+1]) << 8) | + ((u32_t)ctx->in[ii]); + } + transform (ctx->buf, in); /* store buffer in digest */ for (i = 0, ii = 0; i < 4; i++, ii += 4) { - mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); - mdContext->digest[ii+1] = - (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); - mdContext->digest[ii+2] = - (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); - mdContext->digest[ii+3] = - (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + ctx->digest[ii] = (unsigned char)(ctx->buf[i] & 0xFF); + ctx->digest[ii+1] = + (unsigned char)((ctx->buf[i] >> 8) & 0xFF); + ctx->digest[ii+2] = + (unsigned char)((ctx->buf[i] >> 16) & 0xFF); + ctx->digest[ii+3] = + (unsigned char)((ctx->buf[i] >> 24) & 0xFF); } - memcpy(hash, mdContext->digest, 16); + SMEMCPY(hash, ctx->digest, 16); } /* Basic MD5 step. Transforms buf based on in. */ -static void Transform (u32_t *buf, u32_t *in) +static void +transform (u32_t *buf, u32_t *in) { u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; @@ -302,5 +313,6 @@ static void Transform (u32_t *buf, u32_t buf[3] += d; } -#endif +#endif /* CHAP_SUPPORT || MD5_SUPPORT */ +#endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/md5.h b/packages/net/lwip_tcpip/current/src/netif/ppp/md5.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/md5.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/md5.h @@ -42,14 +42,14 @@ /* Data structure for MD5 (Message-Digest) computation */ typedef struct { - u32_t i[2]; /* number of _bits_ handled mod 2^64 */ - u32_t buf[4]; /* scratch buffer */ - unsigned char in[64]; /* input buffer */ - unsigned char digest[16]; /* actual digest after MD5Final call */ -} MD5_CTX; + u32_t i[2]; /* number of _bits_ handled mod 2^64 */ + u32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after md5_final call */ +} md5_ctx; -void MD5Init (MD5_CTX *mdContext); -void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); -void MD5Final (unsigned char hash[], MD5_CTX *mdContext); +void md5_init(md5_ctx *ctx); +void md5_update(md5_ctx *ctx, unsigned char *buf, unsigned int len); +void md5_final(unsigned char hash[], md5_ctx *ctx); #endif /* MD5_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/pap.c b/packages/net/lwip_tcpip/current/src/netif/ppp/pap.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/pap.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/pap.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-12 Guy Lancaster , Global Election Systems Inc. -* Original. +* Original. *****************************************************************************/ /* * upap.c - User/Password Authentication Protocol. @@ -49,13 +49,18 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#include "ppp.h" +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" +#include "timesys.h" + #include "auth.h" #include "pap.h" -#include "pppdebug.h" - - -#if PAP_SUPPORT > 0 /***********************************/ /*** LOCAL FUNCTION DECLARATIONS ***/ @@ -63,49 +68,47 @@ /* * Protocol entry points. */ -static void upap_init (int); -static void upap_lowerup (int); +static void upap_init (int); +static void upap_lowerup (int); static void upap_lowerdown (int); -static void upap_input (int, u_char *, int); -static void upap_protrej (int); +static void upap_input (int, u8_t *, int); +static void upap_protrej (int); -static void upap_timeout (void *); -static void upap_reqtimeout (void *); -static void upap_rauthreq (upap_state *, u_char *, int, int); -static void upap_rauthack (upap_state *, u_char *, int, int); -static void upap_rauthnak (upap_state *, u_char *, int, int); -static void upap_sauthreq (upap_state *); -static void upap_sresp (upap_state *, u_char, u_char, char *, int); - - +static void upap_timeout (void *); +static void upap_reqtimeout(void *); +static void upap_rauthreq (upap_state *, u8_t *, int, int); +static void upap_rauthack (upap_state *, u8_t *, int, int); +static void upap_rauthnak (upap_state *, u8_t *, int, int); +static void upap_sauthreq (upap_state *); +static void upap_sresp (upap_state *, u8_t, u8_t, char *, int); /******************************/ /*** PUBLIC DATA STRUCTURES ***/ /******************************/ struct protent pap_protent = { - PPP_PAP, - upap_init, - upap_input, - upap_protrej, - upap_lowerup, - upap_lowerdown, - NULL, - NULL, + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, #if 0 - upap_printpkt, - NULL, + upap_printpkt, + NULL, #endif - 1, - "PAP", + 1, + "PAP", #if 0 - NULL, - NULL, - NULL + NULL, + NULL, + NULL #endif }; -upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ @@ -115,15 +118,16 @@ upap_state upap[NUM_PPP]; /* UPAP state /* * Set the default login name and password for the pap sessions */ -void upap_setloginpasswd(int unit, const char *luser, const char *lpassword) +void +upap_setloginpasswd(int unit, const char *luser, const char *lpassword) { - upap_state *u = &upap[unit]; - - /* Save the username and password we're given */ - u->us_user = luser; - u->us_userlen = strlen(luser); - u->us_passwd = lpassword; - u->us_passwdlen = strlen(lpassword); + upap_state *u = &upap[unit]; + + /* Save the username and password we're given */ + u->us_user = luser; + u->us_userlen = strlen(luser); + u->us_passwd = lpassword; + u->us_passwdlen = strlen(lpassword); } @@ -132,25 +136,26 @@ void upap_setloginpasswd(int unit, const * * Set new state and send authenticate's. */ -void upap_authwithpeer(int unit, char *user, char *password) +void +upap_authwithpeer(int unit, char *user, char *password) { - upap_state *u = &upap[unit]; - - UPAPDEBUG((LOG_INFO, "upap_authwithpeer: %d user=%s password=%s s=%d\n", - unit, user, password, u->us_clientstate)); - - upap_setloginpasswd(unit, user, password); + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_authwithpeer: %d user=%s password=%s s=%d\n", + unit, user, password, u->us_clientstate)); + + upap_setloginpasswd(unit, user, password); - u->us_transmits = 0; - - /* Lower layer up yet? */ - if (u->us_clientstate == UPAPCS_INITIAL || - u->us_clientstate == UPAPCS_PENDING) { - u->us_clientstate = UPAPCS_PENDING; - return; - } - - upap_sauthreq(u); /* Start protocol */ + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ } @@ -159,20 +164,22 @@ void upap_authwithpeer(int unit, char *u * * Set new state. */ -void upap_authpeer(int unit) +void +upap_authpeer(int unit) { - upap_state *u = &upap[unit]; - - /* Lower layer up yet? */ - if (u->us_serverstate == UPAPSS_INITIAL || - u->us_serverstate == UPAPSS_PENDING) { - u->us_serverstate = UPAPSS_PENDING; - return; - } - - u->us_serverstate = UPAPSS_LISTEN; - if (u->us_reqtimeout > 0) - TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } } @@ -183,61 +190,66 @@ void upap_authpeer(int unit) /* * upap_init - Initialize a UPAP unit. */ -static void upap_init(int unit) +static void +upap_init(int unit) { - upap_state *u = &upap[unit]; + upap_state *u = &upap[unit]; - UPAPDEBUG((LOG_INFO, "upap_init: %d\n", unit)); - u->us_unit = unit; - u->us_user = NULL; - u->us_userlen = 0; - u->us_passwd = NULL; - u->us_passwdlen = 0; - u->us_clientstate = UPAPCS_INITIAL; - u->us_serverstate = UPAPSS_INITIAL; - u->us_id = 0; - u->us_timeouttime = UPAP_DEFTIMEOUT; - u->us_maxtransmits = 10; - u->us_reqtimeout = UPAP_DEFREQTIME; + UPAPDEBUG((LOG_INFO, "upap_init: %d\n", unit)); + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; } /* * upap_timeout - Retransmission timer for sending auth-reqs expired. */ -static void upap_timeout(void *arg) +static void +upap_timeout(void *arg) { - upap_state *u = (upap_state *) arg; - - UPAPDEBUG((LOG_INFO, "upap_timeout: %d timeout %d expired s=%d\n", - u->us_unit, u->us_timeouttime, u->us_clientstate)); - - if (u->us_clientstate != UPAPCS_AUTHREQ) - return; - - if (u->us_transmits >= u->us_maxtransmits) { - /* give up in disgust */ - UPAPDEBUG((LOG_ERR, "No response to PAP authenticate-requests\n")); - u->us_clientstate = UPAPCS_BADAUTH; - auth_withpeer_fail(u->us_unit, PPP_PAP); - return; - } - - upap_sauthreq(u); /* Send Authenticate-Request */ + upap_state *u = (upap_state *) arg; + + UPAPDEBUG((LOG_INFO, "upap_timeout: %d timeout %d expired s=%d\n", + u->us_unit, u->us_timeouttime, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { + return; + } + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + UPAPDEBUG((LOG_ERR, "No response to PAP authenticate-requests\n")); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, PPP_PAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request */ } /* * upap_reqtimeout - Give up waiting for the peer to send an auth-req. */ -static void upap_reqtimeout(void *arg) +static void +upap_reqtimeout(void *arg) { - upap_state *u = (upap_state *) arg; - - if (u->us_serverstate != UPAPSS_LISTEN) - return; /* huh?? */ - - auth_peer_fail(u->us_unit, PPP_PAP); - u->us_serverstate = UPAPSS_BADAUTH; + upap_state *u = (upap_state *) arg; + + if (u->us_serverstate != UPAPSS_LISTEN) { + return; /* huh?? */ + } + + auth_peer_fail(u->us_unit, PPP_PAP); + u->us_serverstate = UPAPSS_BADAUTH; } @@ -246,25 +258,27 @@ static void upap_reqtimeout(void *arg) * * Start authenticating if pending. */ -static void upap_lowerup(int unit) +static void +upap_lowerup(int unit) { - upap_state *u = &upap[unit]; - - UPAPDEBUG((LOG_INFO, "upap_lowerup: %d s=%d\n", unit, u->us_clientstate)); - - if (u->us_clientstate == UPAPCS_INITIAL) - u->us_clientstate = UPAPCS_CLOSED; - else if (u->us_clientstate == UPAPCS_PENDING) { - upap_sauthreq(u); /* send an auth-request */ - } - - if (u->us_serverstate == UPAPSS_INITIAL) - u->us_serverstate = UPAPSS_CLOSED; - else if (u->us_serverstate == UPAPSS_PENDING) { - u->us_serverstate = UPAPSS_LISTEN; - if (u->us_reqtimeout > 0) - TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); - } + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_lowerup: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_INITIAL) { + u->us_clientstate = UPAPCS_CLOSED; + } else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) { + u->us_serverstate = UPAPSS_CLOSED; + } else if (u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } + } } @@ -273,19 +287,22 @@ static void upap_lowerup(int unit) * * Cancel all timeouts. */ -static void upap_lowerdown(int unit) +static void +upap_lowerdown(int unit) { - upap_state *u = &upap[unit]; - - UPAPDEBUG((LOG_INFO, "upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); - - if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ - UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ - if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) - UNTIMEOUT(upap_reqtimeout, u); - - u->us_clientstate = UPAPCS_INITIAL; - u->us_serverstate = UPAPSS_INITIAL; + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + } + if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; } @@ -294,295 +311,289 @@ static void upap_lowerdown(int unit) * * This shouldn't happen. In any case, pretend lower layer went down. */ -static void upap_protrej(int unit) +static void +upap_protrej(int unit) { - upap_state *u = &upap[unit]; - - if (u->us_clientstate == UPAPCS_AUTHREQ) { - UPAPDEBUG((LOG_ERR, "PAP authentication failed due to protocol-reject\n")); - auth_withpeer_fail(unit, PPP_PAP); - } - if (u->us_serverstate == UPAPSS_LISTEN) { - UPAPDEBUG((LOG_ERR, "PAP authentication of peer failed (protocol-reject)\n")); - auth_peer_fail(unit, PPP_PAP); - } - upap_lowerdown(unit); + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + UPAPDEBUG((LOG_ERR, "PAP authentication failed due to protocol-reject\n")); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + UPAPDEBUG((LOG_ERR, "PAP authentication of peer failed (protocol-reject)\n")); + auth_peer_fail(unit, PPP_PAP); + } + upap_lowerdown(unit); } /* * upap_input - Input UPAP packet. */ -static void upap_input(int unit, u_char *inpacket, int l) +static void +upap_input(int unit, u8_t *inpacket, int l) { - upap_state *u = &upap[unit]; - u_char *inp; - u_char code, id; - int len; - - /* - * Parse header (code, id and length). - * If packet too short, drop it. - */ - inp = inpacket; - if (l < UPAP_HEADERLEN) { - UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header.\n")); - return; - } - GETCHAR(code, inp); - GETCHAR(id, inp); - GETSHORT(len, inp); - if (len < UPAP_HEADERLEN) { - UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length.\n")); - return; - } - if (len > l) { - UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet.\n")); - return; - } - len -= UPAP_HEADERLEN; - - /* - * Action depends on code. - */ - switch (code) { - case UPAP_AUTHREQ: - upap_rauthreq(u, inp, id, len); - break; - - case UPAP_AUTHACK: - upap_rauthack(u, inp, id, len); - break; - - case UPAP_AUTHNAK: - upap_rauthnak(u, inp, id, len); - break; - - default: /* XXX Need code reject */ - break; - } + upap_state *u = &upap[unit]; + u8_t *inp; + u8_t code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length.\n")); + return; + } + if (len > l) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet.\n")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } } /* * upap_rauth - Receive Authenticate. */ -static void upap_rauthreq( - upap_state *u, - u_char *inp, - int id, - int len -) +static void +upap_rauthreq(upap_state *u, u8_t *inp, int id, int len) { - u_char ruserlen, rpasswdlen; - char *ruser, *rpasswd; - int retcode; - char *msg; - int msglen; - - UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.\n", id)); - - if (u->us_serverstate < UPAPSS_LISTEN) - return; - - /* - * If we receive a duplicate authenticate-request, we are - * supposed to return the same status as for the first request. - */ - if (u->us_serverstate == UPAPSS_OPEN) { - upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ - return; - } - if (u->us_serverstate == UPAPSS_BADAUTH) { - upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ - return; - } - - /* - * Parse user/passwd. - */ - if (len < sizeof (u_char)) { - UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); - return; - } - GETCHAR(ruserlen, inp); - len -= sizeof (u_char) + ruserlen + sizeof (u_char); - if (len < 0) { - UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); - return; - } - ruser = (char *) inp; - INCPTR(ruserlen, inp); - GETCHAR(rpasswdlen, inp); - if (len < rpasswdlen) { - UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); - return; - } - rpasswd = (char *) inp; - - /* - * Check the username and password given. - */ - retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, - rpasswdlen, &msg, &msglen); - BZERO(rpasswd, rpasswdlen); - - upap_sresp(u, retcode, id, msg, msglen); - - if (retcode == UPAP_AUTHACK) { - u->us_serverstate = UPAPSS_OPEN; - auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); - } else { - u->us_serverstate = UPAPSS_BADAUTH; - auth_peer_fail(u->us_unit, PPP_PAP); - } - - if (u->us_reqtimeout > 0) - UNTIMEOUT(upap_reqtimeout, u); + u8_t ruserlen, rpasswdlen; + char *ruser, *rpasswd; + int retcode; + char *msg; + int msglen; + + UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.\n", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) { + return; + } + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < sizeof (u8_t)) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u8_t) + ruserlen + sizeof (u8_t); + if (len < 0) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } } /* * upap_rauthack - Receive Authenticate-Ack. */ -static void upap_rauthack( - upap_state *u, - u_char *inp, - int id, - int len -) +static void +upap_rauthack(upap_state *u, u8_t *inp, int id, int len) { - u_char msglen; - char *msg; - - UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); - - if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ - return; - - /* - * Parse message. - */ - if (len < sizeof (u_char)) { - UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); - return; - } - GETCHAR(msglen, inp); - len -= sizeof (u_char); - if (len < msglen) { - UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); - return; - } - msg = (char *) inp; - PRINTMSG(msg, msglen); - - u->us_clientstate = UPAPCS_OPEN; - - auth_withpeer_success(u->us_unit, PPP_PAP); + u8_t msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u8_t)) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u8_t); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); } /* * upap_rauthnak - Receive Authenticate-Nakk. */ -static void upap_rauthnak( - upap_state *u, - u_char *inp, - int id, - int len -) +static void +upap_rauthnak(upap_state *u, u8_t *inp, int id, int len) { - u_char msglen; - char *msg; - - UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); - - if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ - return; - - /* - * Parse message. - */ - if (len < sizeof (u_char)) { - UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); - return; - } - GETCHAR(msglen, inp); - len -= sizeof (u_char); - if (len < msglen) { - UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); - return; - } - msg = (char *) inp; - PRINTMSG(msg, msglen); - - u->us_clientstate = UPAPCS_BADAUTH; - - UPAPDEBUG((LOG_ERR, "PAP authentication failed\n")); - auth_withpeer_fail(u->us_unit, PPP_PAP); + u8_t msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u8_t)) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); + } else { + GETCHAR(msglen, inp); + if(msglen > 0) { + len -= sizeof (u8_t); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + u->us_clientstate = UPAPCS_BADAUTH; + + UPAPDEBUG((LOG_ERR, "PAP authentication failed\n")); + auth_withpeer_fail(u->us_unit, PPP_PAP); } /* * upap_sauthreq - Send an Authenticate-Request. */ -static void upap_sauthreq(upap_state *u) +static void +upap_sauthreq(upap_state *u) { - u_char *outp; - int outlen; - - outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) - + u->us_userlen + u->us_passwdlen; - outp = outpacket_buf[u->us_unit]; - - MAKEHEADER(outp, PPP_PAP); - - PUTCHAR(UPAP_AUTHREQ, outp); - PUTCHAR(++u->us_id, outp); - PUTSHORT(outlen, outp); - PUTCHAR(u->us_userlen, outp); - BCOPY(u->us_user, outp, u->us_userlen); - INCPTR(u->us_userlen, outp); - PUTCHAR(u->us_passwdlen, outp); - BCOPY(u->us_passwd, outp, u->us_passwdlen); - - pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); - - UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d\n", u->us_id)); - - TIMEOUT(upap_timeout, u, u->us_timeouttime); - ++u->us_transmits; - u->us_clientstate = UPAPCS_AUTHREQ; + u8_t *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u8_t) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf[u->us_unit]; + + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + ppp_write(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d\n", u->us_id)); + + TIMEOUT(upap_timeout, u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; } /* * upap_sresp - Send a response (ack or nak). */ -static void upap_sresp( - upap_state *u, - u_char code, - u_char id, - char *msg, - int msglen -) +static void +upap_sresp(upap_state *u, u8_t code, u8_t id, char *msg, int msglen) { - u_char *outp; - int outlen; - - outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; - outp = outpacket_buf[u->us_unit]; - MAKEHEADER(outp, PPP_PAP); - - PUTCHAR(code, outp); - PUTCHAR(id, outp); - PUTSHORT(outlen, outp); - PUTCHAR(msglen, outp); - BCOPY(msg, outp, msglen); - pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); - - UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d s=%d\n", - code, id, u->us_clientstate)); + u8_t *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u8_t) + msglen; + outp = outpacket_buf[u->us_unit]; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + ppp_write(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); } #if 0 @@ -590,19 +601,20 @@ static void upap_sresp( * upap_printpkt - print the contents of a PAP packet. */ static int upap_printpkt( - u_char *p, - int plen, - void (*printer) (void *, char *, ...), - void *arg + u8_t *p, + int plen, + void (*printer) (void *, char *, ...), + void *arg ) { - (void)p; - (void)plen; - (void)printer; - (void)arg; - return 0; + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; } -#endif +#endif /* 0 */ #endif /* PAP_SUPPORT */ +#endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/pap.h b/packages/net/lwip_tcpip/current/src/netif/ppp/pap.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/pap.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/pap.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-04 Guy Lancaster , Global Election Systems Inc. -* Original derived from BSD codes. +* Original derived from BSD codes. *****************************************************************************/ /* * upap.h - User/Password Authentication Protocol definitions. @@ -49,45 +49,46 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ - #ifndef PAP_H #define PAP_H +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + /************************* *** PUBLIC DEFINITIONS *** *************************/ /* * Packet header = Code, id, length. */ -#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) +#define UPAP_HEADERLEN (sizeof (u8_t) + sizeof (u8_t) + sizeof (u16_t)) /* * UPAP codes. */ -#define UPAP_AUTHREQ 1 /* Authenticate-Request */ -#define UPAP_AUTHACK 2 /* Authenticate-Ack */ -#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ /* * Client states. */ -#define UPAPCS_INITIAL 0 /* Connection down */ -#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ -#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ -#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ -#define UPAPCS_OPEN 4 /* We've received an Ack */ -#define UPAPCS_BADAUTH 5 /* We've received a Nak */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ /* * Server states. */ -#define UPAPSS_INITIAL 0 /* Connection down */ -#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ -#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ -#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ -#define UPAPSS_OPEN 4 /* We've sent an Ack */ -#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ /************************ @@ -98,18 +99,18 @@ * Each interface is described by upap structure. */ typedef struct upap_state { - int us_unit; /* Interface unit number */ - const char *us_user; /* User */ - int us_userlen; /* User length */ - const char *us_passwd; /* Password */ - int us_passwdlen; /* Password length */ - int us_clientstate; /* Client state */ - int us_serverstate; /* Server state */ - u_char us_id; /* Current id */ - int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ - int us_transmits; /* Number of auth-reqs sent */ - int us_maxtransmits; /* Maximum number of auth-reqs to send */ - int us_reqtimeout; /* Time to wait for auth-req from peer */ + int us_unit; /* Interface unit number */ + const char *us_user; /* User */ + int us_userlen; /* User length */ + const char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u8_t us_id; /* Current id */ + int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ + int us_reqtimeout; /* Time to wait for auth-req from peer */ } upap_state; @@ -120,10 +121,11 @@ typedef struct upap_state { extern upap_state upap[]; void upap_setloginpasswd(int unit, const char *luser, const char *lpassword); -void upap_authwithpeer (int, char *, char *); -void upap_authpeer (int); +void upap_authwithpeer (int, char *, char *); +void upap_authpeer (int); extern struct protent pap_protent; +#endif /* PAP_SUPPORT */ + #endif /* PAP_H */ - diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/ppp.c b/packages/net/lwip_tcpip/current/src/netif/ppp/ppp.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/ppp.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/ppp.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -78,53 +78,78 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ - -#include - -#include "ppp.h" -#if PPP_SUPPORT > 0 + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/ip.h" /* for ip_input() */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" +#include "timesys.h" + #include "randm.h" #include "fsm.h" -#if PAP_SUPPORT > 0 +#if PAP_SUPPORT #include "pap.h" -#endif -#if CHAP_SUPPORT > 0 +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT #include "chap.h" -#endif +#endif /* CHAP_SUPPORT */ #include "ipcp.h" #include "lcp.h" #include "magic.h" #include "auth.h" -#if VJ_SUPPORT > 0 +#if VJ_SUPPORT #include "vj.h" +#endif /* VJ_SUPPORT */ +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ +#if RECORD_SUPPORT +#include "record.h" #endif -#include "pppdebug.h" +#include /*************************/ /*** LOCAL DEFINITIONS ***/ /*************************/ +#define execute_callback(cb, arg) simple_callback(cb, arg) + +/* PPP link states */ +typedef enum { + PLINITIAL, + PLOPEN, + PLRECV, + PLCLOSE, + PLFINISH, + PLIDLE, +} ppp_link_state_t; + /* * The basic PPP frame. */ -#define PPP_ADDRESS(p) (((u_char *)(p))[0]) -#define PPP_CONTROL(p) (((u_char *)(p))[1]) -#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) +#define PPP_ADDRESS(p) (((u8_t *)(p))[0]) +#define PPP_CONTROL(p) (((u8_t *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u8_t *)(p))[2] << 8) + ((u8_t *)(p))[3]) /* PPP packet parser states. Current state indicates operation yet to be * completed. */ typedef enum { - PDIDLE = 0, /* Idle state - waiting. */ - PDSTART, /* Process start flag. */ - PDADDRESS, /* Process address field. */ - PDCONTROL, /* Process control field. */ - PDPROTOCOL1, /* Process protocol field 1. */ - PDPROTOCOL2, /* Process protocol field 2. */ - PDDATA /* Process data byte. */ -} PPPDevStates; + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +} ppp_dev_state_t; -#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07]) +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & ppp_acccm_mask[c & 0x07]) /************************/ /*** LOCAL DATA TYPES ***/ @@ -132,47 +157,52 @@ typedef enum { /* * PPP interface control block. */ -typedef struct PPPControl_s { - char openFlag; /* True when in use. */ - char oldFrame; /* Old framing character for fd. */ - sio_fd_t fd; /* File device ID of port. */ - int kill_link; /* Shut the link down. */ - int sig_hup; /* Carrier lost. */ - int if_up; /* True when the interface is up. */ - int errCode; /* Code indicating why interface is down. */ - struct pbuf *inHead, *inTail; /* The input packet. */ - PPPDevStates inState; /* The input process state. */ - char inEscaped; /* Escape next character. */ - u16_t inProtocol; /* The input protocol code. */ - u16_t inFCS; /* Input Frame Check Sequence value. */ - int mtu; /* Peer's mru */ - int pcomp; /* Does peer accept protocol compression? */ - int accomp; /* Does peer accept addr/ctl compression? */ - u_long lastXMit; /* Time of last transmission. */ - ext_accm inACCM; /* Async-Ctl-Char-Map for input. */ - ext_accm outACCM; /* Async-Ctl-Char-Map for output. */ -#if VJ_SUPPORT > 0 - int vjEnabled; /* Flag indicating VJ compression enabled. */ - struct vjcompress vjComp; /* Van Jabobsen compression header. */ -#endif +typedef struct ppp_control { + char open; /* True when in use. */ +#if PPPOE_SUPPORT + struct netif *ethif; + struct pppoe_softc *pppoe_sc; +#endif /* PPPOE_SUPPORT */ + int if_up; /* True when the interface is up. */ + int err; /* Code indicating why interface is down. */ +#if PPPOS_SUPPORT + sio_fd_t fd; /* File device ID of port. */ + ppp_link_state_t link_state; /* Link state. */ + int kill_link; /* Shut the link down. */ + int sig_hup; /* Carrier lost. */ + struct pbuf *in_head; /* The input packet (head). */ + struct pbuf *in_tail; /* The input packet (tail). */ + ppp_dev_state_t in_state; /* The input process state. */ + char in_escaped; /* Escape next character. */ + u16_t in_proto; /* The input protocol code. */ + u16_t in_fcs; /* Input Frame Check Sequence value. */ +#endif /* PPPOS_SUPPORT */ + int mtu; /* Peer's mru */ + int pcomp; /* Does peer accept protocol compression? */ + int accomp; /* Does peer accept addr/ctl compression? */ + u32_t last_xmit; /* Time of last transmission. */ + ext_accm in_accm; /* Async-Ctl-Char-Map for input. */ + ext_accm out_accm; /* Async-Ctl-Char-Map for output. */ +#if PPPOS_SUPPORT && VJ_SUPPORT + int vj_enabled; /* Flag indicating VJ compression enabled. */ + struct vjcompress vj_comp; /* Van Jabobsen compression header. */ +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ - struct netif netif; - - struct ppp_addrs addrs; + struct netif netif; + struct ppp_addrs addrs; - void (*linkStatusCB)(void *ctx, int errCode, void *arg); - void *linkStatusCtx; + ppp_status_cb_t status_cb; + void *status_ctx; -} PPPControl; +} ppp_control; /* * Ioctl definitions. */ - struct npioctl { - int protocol; /* PPP procotol, e.g. PPP_IP */ - enum NPmode mode; + int protocol; /* PPP procotol, e.g. PPP_IP */ + enum ppp_np_mode mode; }; @@ -180,17 +210,20 @@ struct npioctl { /***********************************/ /*** LOCAL FUNCTION DECLARATIONS ***/ /***********************************/ -static void pppMain(void *pd); -static void pppDrop(PPPControl *pc); -static void pppInProc(int pd, u_char *s, int l); +#if PPPOS_SUPPORT +static void ppp_loop(void *arg); +static void ppp_main(void *pd); +static void ppp_drop(ppp_control *pc); +static void ppp_in_proc(int pd, u8_t *s, int l); +#endif /* PPPOS_SUPPORT */ /******************************/ /*** PUBLIC DATA STRUCTURES ***/ /******************************/ -u_long subnetMask; +u32_t subnetMask; -static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */ +static ppp_control ppp_controls[NUM_PPP]; /* The PPP interface control blocks. */ /* * PPP Data Link Layer "protocol" table. @@ -198,86 +231,168 @@ static PPPControl pppControl[NUM_PPP]; / * The last entry must be NULL. */ struct protent *ppp_protocols[] = { - &lcp_protent, -#if PAP_SUPPORT > 0 - &pap_protent, -#endif -#if CHAP_SUPPORT > 0 - &chap_protent, -#endif -#if CBCP_SUPPORT > 0 - &cbcp_protent, -#endif - &ipcp_protent, -#if CCP_SUPPORT > 0 - &ccp_protent, -#endif - NULL + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif /* CBCP_SUPPORT */ + &ipcp_protent, +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ + NULL }; - /* - * Buffers for outgoing packets. This must be accessed only from the appropriate - * PPP task so that it doesn't need to be protected to avoid collisions. + * Buffers for incoming and outgoing packets. This must be accessed only from + * the appropriate PPP task so that it doesn't need to be protected to avoid + * collisions. */ -u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; - +#define PPP_PACKET_SIZE (PPP_MRU + PPP_HDRLEN) +static u8_t inpacket_data[NUM_PPP * PPP_PACKET_SIZE]; +static u8_t outpacket_data[NUM_PPP * PPP_PACKET_SIZE]; +u8_t *inpacket_buf[NUM_PPP]; +u8_t *outpacket_buf[NUM_PPP]; /*****************************/ /*** LOCAL DATA STRUCTURES ***/ /*****************************/ +#if PPPOS_SUPPORT /* * FCS lookup table as calculated by genfcstab. */ -static const u_short fcstab[256] = { - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +static const u16_t fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; /* PPP's Asynchronous-Control-Character-Map. The mask array is used * to select the specific bit for a character. */ -static u_char pppACCMMask[] = { - 0x01, - 0x02, - 0x04, - 0x08, - 0x10, - 0x20, - 0x40, - 0x80 -}; +static u8_t ppp_acccm_mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + +#endif /* PPPOS_SUPPORT */ + + +static err_t simple_callback(void (*cb)(void *arg), void *arg) +{ + cb(arg); + return ERR_OK; +} + + +void +ppp_link_terminated(int pd) +{ + ppp_control *pc = &ppp_controls[pd]; + + PPPDEBUG((LOG_DEBUG, "ppp_link_terminated: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if (pc->ethif) { + pppoe_disconnect(pc->pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + sio_read_abort(pc->fd); +#endif /* PPPOS_SUPPORT */ + } +} + +void +ppp_link_down(int pd) +{ + ppp_control *pc = &ppp_controls[pd]; + + PPPDEBUG((LOG_DEBUG, "ppp_link_down: unit %d\n", pd)); +#if PPPOE_SUPPORT + if (pc->ethif) { + pppoe_disconnect(pc->pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + sio_read_abort(pc->fd); +#endif /* PPPOS_SUPPORT */ + } +} + +/* these callbacks are necessary because lcp_* functions + must be called in the same context as ppp_input(), + namely the tcpip_thread(), essentially because + they manipulate timeouts which are thread-private +*/ + +static void +ppp_start_cb(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "ppp_start_cb: unit %d\n", pd)); + lcp_lowerup(pd); + lcp_open(pd); /* Start protocol */ +} + +static void +ppp_stop_cb(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "ppp_stop_cb: unit %d\n", pd)); + + /* FIXME SK added to disable lcp timeouts */ + lcp_lowerdown(pd); + link_terminated(pd); + + lcp_close(pd, "User request"); +} + +static void +ppp_hup_cb(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "ppp_hup_cb: unit %d\n", pd)); + lcp_lowerdown(pd); + link_terminated(pd); +} /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ @@ -286,94 +401,136 @@ static u_char pppACCMMask[] = { struct ppp_settings ppp_settings; -void pppInit(void) +err_t +ppp_init(void) { - struct protent *protp; - int i, j; - - memset(&ppp_settings, 0, sizeof(ppp_settings)); - ppp_settings.usepeerdns = 1; - pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL); + struct protent *protp; + int i, j; + err_t err; - magicInit(); + err = ppp_timesys_init(); + if (err != ERR_OK) + return err; + + memset(&ppp_settings, 0, sizeof(ppp_settings)); + ppp_settings.usepeerdns = 1; + ppp_set_auth(PPPAUTHTYPE_NONE, NULL, NULL); - for (i = 0; i < NUM_PPP; i++) { - pppControl[i].openFlag = 0; + magic_init(); + +#if RECORD_SUPPORT + record_init(); +#endif - subnetMask = htonl(0xffffff00); + for (i = 0; i < NUM_PPP; i++) { + ppp_controls[i].open = 0; + + subnetMask = htonl(0xffffff00); - /* - * Initialize to the standard option set. - */ - for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) - (*protp->init)(i); + inpacket_buf[i] = &inpacket_data[i * PPP_PACKET_SIZE]; + outpacket_buf[i] = &outpacket_data[i * PPP_PACKET_SIZE]; + + /* + * Initialize to the standard option set. + */ + for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) { + (*protp->init)(i); } + } #if LINK_STATS - /* Clear the statistics. */ - memset(&lwip_stats.link, 0, sizeof(lwip_stats.link)); + /** @todo already done in stats_init (in fact, zeroed at boot). So, remove it? */ + /* Clear the statistics. */ + memset(&lwip_stats.link, 0, sizeof(lwip_stats.link)); +#endif /* LINK_STATS */ + +#if PPPOE_SUPPORT + pppoe_init(); +#endif /* PPPOE_SUPPORT */ + + return ERR_OK; +} + + +/* + * This should be called every PPP_TMR_INTERVAL milliseconds. + */ +void +ppp_tmr(void) +{ + ppp_timesys_update(PPP_TMR_INTERVAL); +#if RECORD_SUPPORT + record_update(PPP_TMR_INTERVAL); #endif } -void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) + +void +ppp_set_auth(enum ppp_auth_type auth_type, const char *user, const char *passwd) { - switch(authType) { - case PPPAUTHTYPE_NONE: - default: + switch (auth_type) { + case PPPAUTHTYPE_NONE: + default: #ifdef LWIP_PPP_STRICT_PAP_REJECT - ppp_settings.refuse_pap = 1; -#else - /* some providers request pap and accept an empty login/pw */ - ppp_settings.refuse_pap = 0; -#endif - ppp_settings.refuse_chap = 1; - break; - case PPPAUTHTYPE_ANY: -/* Warning: Using PPPAUTHTYPE_ANY might have security consequences. - * RFC 1994 says: - * - * In practice, within or associated with each PPP server, there is a - * database which associates "user" names with authentication - * information ("secrets"). It is not anticipated that a particular - * named user would be authenticated by multiple methods. This would - * make the user vulnerable to attacks which negotiate the least secure - * method from among a set (such as PAP rather than CHAP). If the same - * secret was used, PAP would reveal the secret to be used later with - * CHAP. - * - * Instead, for each user name there should be an indication of exactly - * one method used to authenticate that user name. If a user needs to - * make use of different authentication methods under different - * circumstances, then distinct user names SHOULD be employed, each of - * which identifies exactly one authentication method. - * - */ - ppp_settings.refuse_pap = 0; - ppp_settings.refuse_chap = 0; - break; - case PPPAUTHTYPE_PAP: - ppp_settings.refuse_pap = 0; - ppp_settings.refuse_chap = 1; - break; - case PPPAUTHTYPE_CHAP: - ppp_settings.refuse_pap = 1; - ppp_settings.refuse_chap = 0; - break; - } + ppp_settings.refuse_pap = 1; +#else /* LWIP_PPP_STRICT_PAP_REJECT */ + /* some providers request pap and accept an empty login/pw */ + ppp_settings.refuse_pap = 0; +#endif /* LWIP_PPP_STRICT_PAP_REJECT */ + ppp_settings.refuse_chap = 1; + break; - if(user) { - strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1); - ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0'; - } else - ppp_settings.user[0] = '\0'; + case PPPAUTHTYPE_ANY: + /* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 0; + break; - if(passwd) { - strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1); - ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0'; - } else - ppp_settings.passwd[0] = '\0'; + case PPPAUTHTYPE_PAP: + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_CHAP: + ppp_settings.refuse_pap = 1; + ppp_settings.refuse_chap = 0; + break; + } + + if (user) { + strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1); + ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0'; + } else { + ppp_settings.user[0] = '\0'; + } + + if (passwd) { + strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1); + ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0'; + } else { + ppp_settings.passwd[0] = '\0'; + } } +#if PPPOS_SUPPORT /* Open a new PPP connection using the given I/O device. * This initializes the PPP control block but does not * attempt to negotiate the LCP session. If this port @@ -381,472 +538,769 @@ void pppSetAuth(enum pppAuthType authTyp * established before calling this. * Return a new PPP connection descriptor on success or * an error code (negative) on failure. */ -int pppOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +int +ppp_open_serial(sio_fd_t fd, ppp_status_cb_t status_cb, void *status_ctx) { - PPPControl *pc; - int pd; + ppp_control *pc; + int pd; + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && ppp_controls[pd].open; pd++); + if (pd >= NUM_PPP) + return PPPERR_OPEN; - /* Find a free PPP session descriptor. Critical region? */ - for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); - if (pd >= NUM_PPP) - pd = PPPERR_OPEN; - else - pppControl[pd].openFlag = !0; + /* Init state */ + pc = &ppp_controls[pd]; + pc->open = 1; + pc->fd = fd; +#if PPPOE_SUPPORT + pc->ethif= NULL; +#endif /* PPPOE_SUPPORT */ + pc->link_state = PLINITIAL; + pc->kill_link = 0; + pc->sig_hup = 0; + pc->if_up = 0; + pc->err = 0; + pc->in_state = PDIDLE; + pc->in_head = NULL; + pc->in_tail = NULL; + pc->in_escaped = 0; + pc->last_xmit = 0; - /* Launch a deamon thread. */ - if (pd >= 0) { - - pppControl[pd].openFlag = 1; + lcp_init(pd); - lcp_init(pd); - pc = &pppControl[pd]; - pc->fd = fd; - pc->kill_link = 0; - pc->sig_hup = 0; - pc->if_up = 0; - pc->errCode = 0; - pc->inState = PDIDLE; - pc->inHead = NULL; - pc->inTail = NULL; - pc->inEscaped = 0; - pc->lastXMit = 0; +#if VJ_SUPPORT + pc->vj_enabled = 0; + vj_compress_init(&pc->vj_comp); +#endif /* VJ_SUPPORT */ + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + memset(pc->in_accm, 0, sizeof(ext_accm)); + pc->in_accm[15] = 0x60; + memset(pc->out_accm, 0, sizeof(ext_accm)); + pc->out_accm[15] = 0x60; + + pc->status_cb = status_cb; + pc->status_ctx = status_ctx; -#if VJ_SUPPORT > 0 - pc->vjEnabled = 0; - vj_compress_init(&pc->vjComp); + sys_thread_new(PPP_THREAD_NAME, ppp_loop, (void *) pd, + PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO); + +#if NO_SYS == 0 + if (!status_cb) { + while (pd >= 0 && !pc->if_up) { + sys_msleep(500); + if (lcp_phase[pd] == PHASE_DEAD) { + ppp_close(pd); + if (pc->err) { + pd = pc->err; + } else { + pd = PPPERR_CONNECT; + } + } + } + } #endif - /* - * Default the in and out accm so that escape and flag characters - * are always escaped. - */ - memset(pc->inACCM, 0, sizeof(ext_accm)); - pc->inACCM[15] = 0x60; - memset(pc->outACCM, 0, sizeof(ext_accm)); - pc->outACCM[15] = 0x60; + return pd; +} +#endif /* PPPOS_SUPPORT */ - pc->linkStatusCB = linkStatusCB; - pc->linkStatusCtx = linkStatusCtx; +#if PPPOE_SUPPORT +static void pppOverEthernetLinkStatusCB(int pd, int up); - sys_thread_new(pppMain, (void*)pd, PPP_THREAD_PRIO); - if(!linkStatusCB) { - while(pd >= 0 && !pc->if_up) { - sys_msleep(500); - if (lcp_phase[pd] == PHASE_DEAD) { - pppClose(pd); - if (pc->errCode) - pd = pc->errCode; - else - pd = PPPERR_CONNECT; - } - } - } - } - return pd; +void +pppOverEthernetClose(int pd) +{ + ppp_control* pc = &ppp_controls[pd]; + + /* *TJL* There's no lcp_deinit */ + lcp_close(pd, NULL); + + pppoe_destroy(&pc->netif); } -/* Close a PPP connection and release the descriptor. +int ppp_open_ethernet(struct netif *ethif, + const char *service_name, const char *concentrator_name, + ppp_status_cb_t status_cb, void *status_ctx) +{ + ppp_control *pc; + int pd; + + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && ppp_controls[pd].open != 0; pd++); + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + ppp_controls[pd].open = 1; + } + + /* Launch a deamon thread. */ + if (pd >= 0) { + + ppp_controls[pd].open = 1; + + lcp_init(pd); + + lcp_wantoptions[pd].mru = PPPOE_MAXMTU; + lcp_wantoptions[pd].neg_asyncmap = 0; + lcp_wantoptions[pd].neg_pcompression = 0; + lcp_wantoptions[pd].neg_accompression = 0; + + lcp_allowoptions[pd].mru = PPPOE_MAXMTU; + lcp_allowoptions[pd].neg_asyncmap = 0; + lcp_allowoptions[pd].neg_pcompression = 0; + lcp_allowoptions[pd].neg_accompression = 0; + + pc = &ppp_controls[pd]; + pc->if_up = 0; + pc->err = 0; + pc->last_xmit = 0; +#if PPPOS_SUPPORT + pc->kill_link = 0; + pc->sig_hup = 0; + pc->in_state = PDIDLE; + pc->in_head = NULL; + pc->in_tail = NULL; + pc->in_escaped = 0; +#if VJ_SUPPORT + pc->vj_enabled = 0; +#endif /* VJ_SUPPORT */ +#endif /* PPPOS_SUPPORT */ + pc->ethif= ethif; + + memset(pc->in_accm, 0, sizeof(ext_accm)); + memset(pc->out_accm, 0, sizeof(ext_accm)); + + pc->status_cb = status_cb; + pc->status_ctx = status_ctx; + + if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) { + pc->open = 0; + return PPPERR_OPEN; + } + + pppoe_connect(pc->pppoe_sc); + + if(!status_cb) { + while(pd >= 0 && !pc->if_up) { + sys_msleep(500); + if (lcp_phase[pd] == PHASE_DEAD) { + ppp_close(pd); + if (pc->err) { + pd = pc->err; + } else { + pd = PPPERR_CONNECT; + } + } + } + } + } + + return pd; +} +#endif /* PPPOE_SUPPORT */ + + +void ppp_poll(int pd) +{ + ppp_control *pc = &ppp_controls[pd]; + int done = 0; + int c; + + while (!done) { + switch (pc->link_state) { + case PLINITIAL: + pc->link_state = PLOPEN; + break; + case PLOPEN: + /* Start the connection and handle incoming events (packet or timeout). */ + PPPDEBUG((LOG_INFO, "ppp_poll: unit %d: Connecting\n", pd)); + execute_callback(ppp_start_cb, (void *) pd); + pc->link_state = PLRECV; + break; + case PLRECV: + if (lcp_phase[pd] == PHASE_DEAD) { + pc->link_state = PLCLOSE; + break; + } + if (pc->kill_link) { + PPPDEBUG((LOG_DEBUG, "ppp_poll: unit %d kill_link -> ppp_stop_cb\n", pd)); + pc->err = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + execute_callback(ppp_stop_cb, (void *) pd); + pc->kill_link = 0; + break; + } + if (pc->sig_hup) { + PPPDEBUG((LOG_DEBUG, "ppp_poll: unit %d sig_hup -> ppp_hup_cb\n", pd)); + pc->sig_hup = 0; + execute_callback(ppp_hup_cb, (void *) pd); + break; + } + c = sio_tryread(pc->fd, inpacket_buf[pd], PPP_PACKET_SIZE); + if (c > 0) { + ppp_in_proc(pd, inpacket_buf[pd], c); + } else { + done = 1; + } + break; + case PLCLOSE: + PPPDEBUG((LOG_INFO, "ppp_poll: unit %d: PHASE_DEAD\n", pd)); + ppp_drop(pc); /* bug fix #17726 */ + ppp_timesys_reset(); + pc->link_state = PLFINISH; + break; + case PLFINISH: + pc->open = 0; + if (pc->status_cb) + pc->status_cb(pc->status_ctx, pc->err ? pc->err : PPPERR_PROTOCOL, NULL); + pc->link_state = PLIDLE; + break; + case PLIDLE: + done = 1; + break; + } + } +} + + +/* Close a PPP connection and release the descriptor. * Any outstanding packets in the queues are dropped. * Return 0 on success, an error code on failure. */ -int pppClose(int pd) +int +ppp_close(int pd) { - PPPControl *pc = &pppControl[pd]; - int st = 0; + ppp_control *pc = &ppp_controls[pd]; + int st = 0; - /* Disconnect */ - pc->kill_link = !0; - pppMainWakeup(pd); - - if(!pc->linkStatusCB) { - while(st >= 0 && lcp_phase[pd] != PHASE_DEAD) { - sys_msleep(500); - break; - } + /* Disconnect */ +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "ppp_close: unit %d kill_link -> ppp_stop_cb\n", pd)); + pc->err = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + execute_callback(ppp_stop_cb, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pc->kill_link = 1; + sio_read_abort(pc->fd); +#endif /* PPPOS_SUPPORT */ + } + +#if NO_SYS == 0 + if(!pc->status_cb) { + while(st >= 0 && lcp_phase[pd] != PHASE_DEAD) { + sys_msleep(500); + break; } - return st; + } +#endif + + return st; +} + +/* + * Check if the PPP connection is open. + */ +int ppp_is_open(int pd) +{ + return ppp_controls[pd].open; } /* This function is called when carrier is lost on the PPP channel. */ -void pppSigHUP(int pd) +void +ppp_sig_hup(int pd) { - PPPControl *pc = &pppControl[pd]; + ppp_control *pc = &ppp_controls[pd]; +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "ppp_sig_hup: unit %d sig_hup -> ppp_hup_cb\n", pd)); + execute_callback(ppp_hup_cb, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT pc->sig_hup = 1; - pppMainWakeup(pd); + sio_read_abort(pc->fd); +#endif /* PPPOS_SUPPORT */ + } } -static void nPut(PPPControl *pc, struct pbuf *nb) +#if PPPOS_SUPPORT +static void +nPut(ppp_control *pc, struct pbuf *nb) { - struct pbuf *b; - int c; + struct pbuf *b; + int c; + + for(b = nb; b != NULL; b = b->next) { +#if RECORD_SUPPORT + record_out(b->payload, b->len); +#endif + if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) { + PPPDEBUG((LOG_WARNING, + "PPP nPut: incomplete sio_write(%d,, %u) = %d\n", pc->fd, b->len, c)); + LINK_STATS_INC(link.err); + pc->last_xmit = 0; /* prepend PPP_FLAG to next packet */ + break; + } + } - for(b = nb; b != NULL; b = b->next) { - if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) { - PPPDEBUG((LOG_WARNING, - "PPP nPut: incomplete sio_write(%d,, %u) = %d\n", pc->fd, b->len, c)); -#if LINK_STATS - lwip_stats.link.err++; -#endif /* LINK_STATS */ - pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */ - break; - } - } - pbuf_free(nb); - -#if LINK_STATS - lwip_stats.link.xmit++; -#endif /* LINK_STATS */ + pbuf_free(nb); + LINK_STATS_INC(link.xmit); } -/* - * pppAppend - append given character to end of given pbuf. If outACCM +/* + * pppAppend - append given character to end of given pbuf. If out_accm * is not NULL and the character needs to be escaped, do so. * If pbuf is full, append another. * Return the current pbuf. */ -static struct pbuf *pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM) +static struct pbuf * +pppAppend(u8_t c, struct pbuf *nb, ext_accm *out_accm) { - struct pbuf *tb = nb; - - /* Make sure there is room for the character and an escape code. - * Sure we don't quite fill the buffer if the character doesn't - * get escaped but is one character worth complicating this? */ - /* Note: We assume no packet header. */ - if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { - tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); - if (tb) { - nb->next = tb; - } -#if LINK_STATS - else { - lwip_stats.link.memerr++; - } -#endif /* LINK_STATS */ - nb = tb; + struct pbuf *tb = nb; + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + /* Note: We assume no packet header. */ + if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { + tb = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL); + if (tb) { + tb->len = 0; + nb->next = tb; + } else { + LINK_STATS_INC(link.memerr); + } + nb = tb; + } + + if (nb) { + if (out_accm && ESCAPE_P(*out_accm, c)) { + *((u8_t*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u8_t*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u8_t*)nb->payload + nb->len++) = c; } - if (nb) { - if (outACCM && ESCAPE_P(*outACCM, c)) { - *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE; - *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS; - } - else - *((u_char*)nb->payload + nb->len++) = c; - } - - return tb; + } + + return tb; } +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t +pppifOutputOverEthernet(int pd, struct pbuf *p) +{ + ppp_control *pc = &ppp_controls[pd]; + struct pbuf *pb; + u16_t protocol = PPP_IP; + int i=0; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return ERR_MEM; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->last_xmit = ppp_jiffies(); + + if (!pc->pcomp || protocol > 0xFF) { + *((u8_t*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u8_t*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + return PPPERR_DEVICE; + } + + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ /* Send a packet on the given connection. */ -static err_t pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *ipaddr) +static err_t +pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *ipaddr) { - int pd = (int)netif->state; - u_short protocol = PPP_IP; - PPPControl *pc = &pppControl[pd]; - u_int fcsOut = PPP_INITFCS; - struct pbuf *headMB = NULL, *tailMB = NULL, *p; - u_char c; + int pd = (int)netif->state; + u16_t protocol = PPP_IP; + ppp_control *pc = &ppp_controls[pd]; +#if PPPOS_SUPPORT + u32_t fcsOut = PPP_INITFCS; + struct pbuf *headMB = NULL, *tailMB = NULL, *p; + u8_t c; +#endif /* PPPOS_SUPPORT */ + + LWIP_UNUSED_ARG(ipaddr); - (void)ipaddr; + /* Validate parameters. */ + /* We let any protocol value go through - it can't hurt us + * and the peer will just drop it if it's not accepting it. */ + if (pd < 0 || pd >= NUM_PPP || !pc->open || !pb) { + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad parms prot=%d pb=%p\n", + pd, protocol, pb)); + LINK_STATS_INC(link.opterr); + LINK_STATS_INC(link.drop); + return ERR_ARG; + } + + /* Check that the link is up. */ + if (lcp_phase[pd] == PHASE_DEAD) { + PPPDEBUG((LOG_ERR, "pppifOutput[%d]: link not up\n", pd)); + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + return ERR_RTE; + } - /* Validate parameters. */ - /* We let any protocol value go through - it can't hurt us - * and the peer will just drop it if it's not accepting it. */ - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) { - PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad parms prot=%d pb=%p\n", - pd, protocol, pb)); -#if LINK_STATS - lwip_stats.link.opterr++; - lwip_stats.link.drop++; -#endif - return ERR_ARG; - } +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppifOutputOverEthernet(pd, pb); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + /* Grab an output buffer. */ + headMB = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL); + if (headMB == NULL) { + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: first alloc fail\n", pd)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + return ERR_MEM; + } + headMB->len = 0; - /* Check that the link is up. */ - if (lcp_phase[pd] == PHASE_DEAD) { - PPPDEBUG((LOG_ERR, "pppifOutput[%d]: link not up\n", pd)); -#if LINK_STATS - lwip_stats.link.rterr++; - lwip_stats.link.drop++; -#endif - return ERR_RTE; - } - - /* Grab an output buffer. */ - headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); - if (headMB == NULL) { - PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: first alloc fail\n", pd)); -#if LINK_STATS - lwip_stats.link.memerr++; - lwip_stats.link.drop++; -#endif /* LINK_STATS */ - return ERR_MEM; +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pc->vj_enabled) { + switch (vj_compress_tcp(&pc->vj_comp, pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP_PROTOCOL; */ + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad IP packet\n", pd)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + pbuf_free(headMB); + return ERR_VAL; } - -#if VJ_SUPPORT > 0 - /* - * Attempt Van Jacobson header compression if VJ is configured and - * this is an IP packet. - */ - if (protocol == PPP_IP && pc->vjEnabled) { - switch (vj_compress_tcp(&pc->vjComp, pb)) { - case TYPE_IP: - /* No change... - protocol = PPP_IP_PROTOCOL; - */ - break; - case TYPE_COMPRESSED_TCP: - protocol = PPP_VJC_COMP; - break; - case TYPE_UNCOMPRESSED_TCP: - protocol = PPP_VJC_UNCOMP; - break; - default: - PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad IP packet\n", pd)); -#if LINK_STATS - lwip_stats.link.proterr++; - lwip_stats.link.drop++; -#endif - pbuf_free(headMB); - return ERR_VAL; - } - } -#endif - - tailMB = headMB; - - /* Build the PPP header. */ - if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) - tailMB = pppAppend(PPP_FLAG, tailMB, NULL); - pc->lastXMit = sys_jiffies(); - if (!pc->accomp) { - fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS); - tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM); - fcsOut = PPP_FCS(fcsOut, PPP_UI); - tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM); - } - if (!pc->pcomp || protocol > 0xFF) { - c = (protocol >> 8) & 0xFF; - fcsOut = PPP_FCS(fcsOut, c); - tailMB = pppAppend(c, tailMB, &pc->outACCM); + } +#endif /* VJ_SUPPORT */ + + tailMB = headMB; + + /* Build the PPP header. */ + if ((ppp_jiffies() - pc->last_xmit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + + pc->last_xmit = ppp_jiffies(); + if (!pc->accomp) { + fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS); + tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->out_accm); + fcsOut = PPP_FCS(fcsOut, PPP_UI); + tailMB = pppAppend(PPP_UI, tailMB, &pc->out_accm); + } + if (!pc->pcomp || protocol > 0xFF) { + c = (protocol >> 8) & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->out_accm); + } + c = protocol & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->out_accm); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + int n; + u8_t *sPtr; + + sPtr = (u8_t*)p->payload; + n = p->len; + while (n-- > 0) { + c = *sPtr++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->out_accm); } - c = protocol & 0xFF; - fcsOut = PPP_FCS(fcsOut, c); - tailMB = pppAppend(c, tailMB, &pc->outACCM); - - /* Load packet. */ - for(p = pb; p; p = p->next) { - int n; - u_char *sPtr; + } - sPtr = (u_char*)p->payload; - n = p->len; - while (n-- > 0) { - c = *sPtr++; - - /* Update FCS before checking for special characters. */ - fcsOut = PPP_FCS(fcsOut, c); - - /* Copy to output buffer escaping special characters. */ - tailMB = pppAppend(c, tailMB, &pc->outACCM); - } - } + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->out_accm); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->out_accm); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); - /* Add FCS and trailing flag. */ - c = ~fcsOut & 0xFF; - tailMB = pppAppend(c, tailMB, &pc->outACCM); - c = (~fcsOut >> 8) & 0xFF; - tailMB = pppAppend(c, tailMB, &pc->outACCM); - tailMB = pppAppend(PPP_FLAG, tailMB, NULL); - - /* If we failed to complete the packet, throw it away. */ - if (!tailMB) { - PPPDEBUG((LOG_WARNING, - "pppifOutput[%d]: Alloc err - dropping proto=%d\n", - pd, protocol)); - pbuf_free(headMB); -#if LINK_STATS - lwip_stats.link.memerr++; - lwip_stats.link.drop++; -#endif - return ERR_MEM; - } + /* If we failed to complete the packet, throw it away. */ + if (!tailMB) { + PPPDEBUG((LOG_WARNING, + "pppifOutput[%d]: Alloc err - dropping proto=%d\n", + pd, protocol)); + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + return ERR_MEM; + } - /* Send it. */ - PPPDEBUG((LOG_INFO, "pppifOutput[%d]: proto=0x%04X\n", pd, protocol)); + /* Send it. */ + PPPDEBUG((LOG_INFO, "pppifOutput[%d]: proto=0x%04X\n", pd, protocol)); - nPut(pc, headMB); + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ - return ERR_OK; + return ERR_OK; } /* Get and set parameters for the given connection. * Return 0 on success, an error code on failure. */ -int pppIOCtl(int pd, int cmd, void *arg) +int +ppp_ioctl(int pd, int cmd, void *arg) { - PPPControl *pc = &pppControl[pd]; - int st = 0; + ppp_control *pc = &ppp_controls[pd]; + int st = 0; - if (pd < 0 || pd >= NUM_PPP) + if (pd < 0 || pd >= NUM_PPP) { + st = PPPERR_PARAM; + } else { + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (arg) { + *(int *)arg = (int)(pc->if_up); + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLS_ERRCODE: /* Set the PPP error code. */ + if (arg) { + pc->err = *(int *)arg; + } else { st = PPPERR_PARAM; - else { - switch(cmd) { - case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ - if (arg) - *(int *)arg = (int)(pc->if_up); - else - st = PPPERR_PARAM; - break; - case PPPCTLS_ERRCODE: /* Set the PPP error code. */ - if (arg) - pc->errCode = *(int *)arg; - else - st = PPPERR_PARAM; - break; - case PPPCTLG_ERRCODE: /* Get the PPP error code. */ - if (arg) - *(int *)arg = (int)(pc->errCode); - else - st = PPPERR_PARAM; - break; - case PPPCTLG_FD: - if (arg) - *(sio_fd_t *)arg = pc->fd; - else - st = PPPERR_PARAM; - break; - default: - st = PPPERR_PARAM; - break; - } + } + break; + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (arg) { + *(int *)arg = (int)(pc->err); + } else { + st = PPPERR_PARAM; + } + break; +#if PPPOS_SUPPORT + case PPPCTLG_FD: + if (arg) { + *(sio_fd_t *)arg = pc->fd; + } else { + st = PPPERR_PARAM; + } + break; +#endif /* PPPOS_SUPPORT */ + default: + st = PPPERR_PARAM; + break; } - - return st; + } + + return st; } /* * Return the Maximum Transmission Unit for the given PPP connection. */ -u_int pppMTU(int pd) +u32_t +ppp_mtu(int pd) +{ + ppp_control *pc = &ppp_controls[pd]; + u32_t st; + + /* Validate parameters. */ + if (pd < 0 || pd >= NUM_PPP || !pc->open) { + st = 0; + } else { + st = pc->mtu; + } + + return st; +} + +#if PPPOE_SUPPORT +int +ppp_writeOverEthernet(int pd, const u8_t *s, int n) { - PPPControl *pc = &pppControl[pd]; - u_int st; - - /* Validate parameters. */ - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) - st = 0; - else - st = pc->mtu; - - return st; + ppp_control *pc = &ppp_controls[pd]; + struct pbuf *pb; + + /* skip address & flags */ + s += 2; + n -= 2; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + n, PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->last_xmit = ppp_jiffies(); + + SMEMCPY(pb->payload, s, n); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + return PPPERR_DEVICE; + } + + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; } +#endif /* PPPOE_SUPPORT */ /* * Write n characters to a ppp link. * RETURN: >= 0 Number of characters written * -1 Failed to write to device */ -int pppWrite(int pd, const u_char *s, int n) +int +ppp_write(int pd, const u8_t *s, int n) { - PPPControl *pc = &pppControl[pd]; - u_char c; - u_int fcsOut = PPP_INITFCS; - struct pbuf *headMB = NULL, *tailMB; - headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); - if (headMB == NULL) { -#if LINK_STATS - lwip_stats.link.memerr++; - lwip_stats.link.proterr++; -#endif /* LINK_STATS */ - return PPPERR_ALLOC; - } + ppp_control *pc = &ppp_controls[pd]; +#if PPPOS_SUPPORT + u8_t c; + u32_t fcsOut; + struct pbuf *headMB, *tailMB; +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + if(pc->ethif) { + return ppp_writeOverEthernet(pd, s, n); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + headMB = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL); + if (headMB == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + headMB->len = 0; + + tailMB = headMB; + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + if ((ppp_jiffies() - pc->last_xmit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + pc->last_xmit = ppp_jiffies(); - tailMB = headMB; - - /* If the link has been idle, we'll send a fresh flag character to - * flush any noise. */ - if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) - tailMB = pppAppend(PPP_FLAG, tailMB, NULL); - pc->lastXMit = sys_jiffies(); - - /* Load output buffer. */ - while (n-- > 0) { - c = *s++; - - /* Update FCS before checking for special characters. */ - fcsOut = PPP_FCS(fcsOut, c); - - /* Copy to output buffer escaping special characters. */ - tailMB = pppAppend(c, tailMB, &pc->outACCM); - } - - /* Add FCS and trailing flag. */ - c = ~fcsOut & 0xFF; - tailMB = pppAppend(c, tailMB, &pc->outACCM); - c = (~fcsOut >> 8) & 0xFF; - tailMB = pppAppend(c, tailMB, &pc->outACCM); - tailMB = pppAppend(PPP_FLAG, tailMB, NULL); - - /* If we failed to complete the packet, throw it away. - * Otherwise send it. */ - if (!tailMB) { - PPPDEBUG((LOG_WARNING, - "pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len)); -/* "pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ - pbuf_free(headMB); -#if LINK_STATS - lwip_stats.link.memerr++; - lwip_stats.link.proterr++; -#endif /* LINK_STATS */ - return PPPERR_ALLOC; - } + fcsOut = PPP_INITFCS; + /* Load output buffer. */ + while (n-- > 0) { + c = *s++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->out_accm); + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->out_accm); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->out_accm); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); - PPPDEBUG((LOG_INFO, "pppWrite[%d]: len=%d\n", pd, headMB->len)); -/* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ - nPut(pc, headMB); + /* If we failed to complete the packet, throw it away. + * Otherwise send it. */ + if (!tailMB) { + PPPDEBUG((LOG_WARNING, + "ppp_write[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len)); + /*"ppp_write[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } - return PPPERR_NONE; + PPPDEBUG((LOG_INFO, "ppp_write[%d]: len=%d\n", pd, headMB->len)); + /* "ppp_write[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return PPPERR_NONE; } /* * ppp_send_config - configure the transmit characteristics of * the ppp interface. */ -void ppp_send_config( - int unit, - int mtu, - u32_t asyncmap, - int pcomp, - int accomp -) +void +ppp_send_config( int unit, int mtu, u32_t asyncmap, int pcomp, int accomp) { - PPPControl *pc = &pppControl[unit]; - int i; - - pc->mtu = mtu; - pc->pcomp = pcomp; - pc->accomp = accomp; - - /* Load the ACCM bits for the 32 control codes. */ - for (i = 0; i < 32/8; i++) - pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF); - PPPDEBUG((LOG_INFO, "ppp_send_config[%d]: outACCM=%X %X %X %X\n", - unit, - pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3])); + ppp_control *pc = &ppp_controls[unit]; + int i; + + pc->mtu = mtu; + pc->pcomp = pcomp; + pc->accomp = accomp; + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pc->out_accm[i] = (u8_t)((asyncmap >> (8 * i)) & 0xFF); + } + PPPDEBUG((LOG_INFO, "ppp_send_config[%d]: out_accm=%X %X %X %X\n", + unit, + pc->out_accm[0], pc->out_accm[1], pc->out_accm[2], pc->out_accm[3])); } /* * ppp_set_xaccm - set the extended transmit ACCM for the interface. */ -void ppp_set_xaccm(int unit, ext_accm *accm) +void +ppp_set_xaccm(int unit, ext_accm *accm) { - memcpy(pppControl[unit].outACCM, accm, sizeof(ext_accm)); - PPPDEBUG((LOG_INFO, "ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n", - unit, - pppControl[unit].outACCM[0], - pppControl[unit].outACCM[1], - pppControl[unit].outACCM[2], - pppControl[unit].outACCM[3])); + SMEMCPY(ppp_controls[unit].out_accm, accm, sizeof(ext_accm)); + PPPDEBUG((LOG_INFO, "ppp_set_xaccm[%d]: out_accm=%X %X %X %X\n", + unit, + ppp_controls[unit].out_accm[0], + ppp_controls[unit].out_accm[1], + ppp_controls[unit].out_accm[2], + ppp_controls[unit].out_accm[3])); } @@ -854,27 +1308,23 @@ void ppp_set_xaccm(int unit, ext_accm *a * ppp_recv_config - configure the receive-side characteristics of * the ppp interface. */ -void ppp_recv_config( - int unit, - int mru, - u32_t asyncmap, - int pcomp, - int accomp -) +void +ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp) { - PPPControl *pc = &pppControl[unit]; - int i; - - (void)accomp; - (void)pcomp; - (void)mru; + ppp_control *pc = &ppp_controls[unit]; + int i; + + LWIP_UNUSED_ARG(accomp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(mru); - /* Load the ACCM bits for the 32 control codes. */ - for (i = 0; i < 32 / 8; i++) - pc->inACCM[i] = (u_char)(asyncmap >> (i * 8)); - PPPDEBUG((LOG_INFO, "ppp_recv_config[%d]: inACCM=%X %X %X %X\n", - unit, - pc->inACCM[0], pc->inACCM[1], pc->inACCM[2], pc->inACCM[3])); + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32 / 8; i++) { + pc->in_accm[i] = (u8_t)(asyncmap >> (i * 8)); + } + PPPDEBUG((LOG_INFO, "ppp_recv_config[%d]: in_accm=%X %X %X %X\n", + unit, + pc->in_accm[0], pc->in_accm[1], pc->in_accm[2], pc->in_accm[3])); } #if 0 @@ -884,22 +1334,19 @@ void ppp_recv_config( * are OK, 0 if the method is known but the parameters are not OK * (e.g. code size should be reduced), or -1 if the method is unknown. */ -int ccp_test( - int unit, - int opt_len, - int for_transmit, - u_char *opt_ptr -) +int +ccp_test( int unit, int opt_len, int for_transmit, u8_t *opt_ptr) { - return 0; /* XXX Currently no compression. */ + return 0; /* XXX Currently no compression. */ } /* * ccp_flags_set - inform kernel about the current state of CCP. */ -void ccp_flags_set(int unit, int isopen, int isup) +void +ccp_flags_set(int unit, int isopen, int isup) { - /* XXX */ + /* XXX */ } /* @@ -907,23 +1354,25 @@ void ccp_flags_set(int unit, int isopen, * result of an error detected after decompression of a packet, * 0 otherwise. This is necessary because of patent nonsense. */ -int ccp_fatal_error(int unit) +int +ccp_fatal_error(int unit) { - /* XXX */ - return 0; + /* XXX */ + return 0; } #endif /* * get_idle_time - return how long the link has been idle. */ -int get_idle_time(int u, struct ppp_idle *ip) -{ - /* XXX */ - (void)u; - (void)ip; +int +get_idle_time(int u, struct ppp_idle *ip) +{ + /* XXX */ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(ip); - return 0; + return 0; } @@ -935,689 +1384,755 @@ int get_idle_time(int u, struct ppp_idle * network as `addr'. If we find any, we OR in their netmask to the * user-specified netmask. */ -u32_t GetMask(u32_t addr) +u32_t +GetMask(u32_t addr) { - u32_t mask, nmask; - - htonl(addr); - if (IN_CLASSA(addr)) /* determine network mask for address class */ - nmask = IN_CLASSA_NET; - else if (IN_CLASSB(addr)) - nmask = IN_CLASSB_NET; - else - nmask = IN_CLASSC_NET; - /* class D nets are disallowed by bad_ip_adrs */ - mask = subnetMask | htonl(nmask); - - /* XXX - * Scan through the system's network interfaces. - * Get each netmask and OR them into our mask. - */ - - return mask; + u32_t mask, nmask; + + htonl(addr); + if (IN_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IN_CLASSA_NET; + } else if (IN_CLASSB(addr)) { + nmask = IN_CLASSB_NET; + } else { + nmask = IN_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = subnetMask | htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + + return mask; } /* * sifvjcomp - config tcp header compression */ -int sifvjcomp( - int pd, - int vjcomp, - int cidcomp, - int maxcid -) +int +sifvjcomp( int pd, int vjcomp, int cidcomp, int maxcid) { -#if VJ_SUPPORT > 0 - PPPControl *pc = &pppControl[pd]; - - pc->vjEnabled = vjcomp; - pc->vjComp.compressSlot = cidcomp; - pc->vjComp.maxSlotIndex = maxcid; - PPPDEBUG((LOG_INFO, "sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", - vjcomp, cidcomp, maxcid)); -#endif +#if PPPOS_SUPPORT && VJ_SUPPORT + ppp_control *pc = &ppp_controls[pd]; - return 0; + pc->vj_enabled = vjcomp; + pc->vj_comp.compressSlot = cidcomp; + pc->vj_comp.maxSlotIndex = maxcid; + PPPDEBUG((LOG_INFO, "sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", + vjcomp, cidcomp, maxcid)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + return 0; } /* * pppifNetifInit - netif init callback */ -static err_t pppifNetifInit(struct netif *netif) +static err_t +pppifNetifInit(struct netif *netif) { - netif->name[0] = 'p'; - netif->name[1] = 'p'; - netif->output = pppifOutput; - netif->mtu = pppMTU((int)netif->state); - return ERR_OK; + netif->name[0] = 'p'; + netif->name[1] = 'p'; + netif->output = pppifOutput; + netif->mtu = ppp_mtu((int)netif->state); + return ERR_OK; } /* * sifup - Config the interface up and enable IP packets to pass. */ -int sifup(int pd) +int +sifup(int pd) { - PPPControl *pc = &pppControl[pd]; - int st = 1; - - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { - st = 0; - PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + ppp_control *pc = &ppp_controls[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->open) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_remove(&pc->netif); + if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, &pc->addrs.his_ipaddr, (void *)pd, pppifNetifInit, ip_input)) { + netif_set_up(&pc->netif); +#if LWIP_DHCP + /* ugly workaround for storing a reference to the ppp related info*/ + pc->netif.dhcp = (struct dhcp *) &pc->addrs; +#endif /* LWIP_DHCP */ + pc->if_up = 1; + pc->err = PPPERR_NONE; + + PPPDEBUG((LOG_DEBUG, "sifup: unit %d: status_cb=%lx err=%d\n", pd, pc->status_cb, pc->err)); + if(pc->status_cb) { + pc->status_cb(pc->status_ctx, pc->err, &pc->addrs); + } } else { - netif_remove(&pc->netif); - if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, &pc->addrs.his_ipaddr, (void *)pd, pppifNetifInit, ip_input)) { - pc->if_up = 1; - pc->errCode = PPPERR_NONE; + st = 0; + PPPDEBUG((LOG_ERR, "sifup[%d]: netif_add failed\n", pd)); + } + } - PPPDEBUG((LOG_DEBUG, "sifup: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); - if(pc->linkStatusCB) - pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs); - } else { - st = 0; - PPPDEBUG((LOG_ERR, "sifup[%d]: netif_add failed\n", pd)); - } - } - - return st; + return st; } /* * sifnpmode - Set the mode for handling packets for a given NP. */ -int sifnpmode(int u, int proto, enum NPmode mode) +int +sifnpmode(int u, int proto, enum ppp_np_mode mode) { - (void)u; - (void)proto; - (void)mode; - return 0; + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; } /* * sifdown - Config the interface down and disable IP. */ -int sifdown(int pd) +int +sifdown(int pd) { - PPPControl *pc = &pppControl[pd]; - int st = 1; - - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { - st = 0; - PPPDEBUG((LOG_WARNING, "sifdown[%d]: bad parms\n", pd)); - } else { - pc->if_up = 0; - netif_remove(&pc->netif); - PPPDEBUG((LOG_DEBUG, "sifdown: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); - if(pc->linkStatusCB) - pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL); - } - return st; + ppp_control *pc = &ppp_controls[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->open) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifdown[%d]: bad parms\n", pd)); + } else { + pc->if_up = 0; + netif_remove(&pc->netif); + PPPDEBUG((LOG_DEBUG, "sifdown: unit %d: status_cb=%lx err=%d\n", pd, pc->status_cb, pc->err)); + if(pc->status_cb) { + pc->status_cb(pc->status_ctx, PPPERR_CONNECT, NULL); + } + } + return st; } -/* +/** * sifaddr - Config the interface IP addresses and netmask. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h His IP address ??? + * @param m IP subnet mask ??? + * @param ns1 Primary DNS + * @param ns2 Secondary DNS */ -int sifaddr( - int pd, /* Interface unit ??? */ - u32_t o, /* Our IP address ??? */ - u32_t h, /* His IP address ??? */ - u32_t m, /* IP subnet mask ??? */ - u32_t ns1, /* Primary DNS */ - u32_t ns2 /* Secondary DNS */ -) +int +sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2) { - PPPControl *pc = &pppControl[pd]; - int st = 1; - - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { - st = 0; - PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); - } else { - memcpy(&pc->addrs.our_ipaddr, &o, sizeof(o)); - memcpy(&pc->addrs.his_ipaddr, &h, sizeof(h)); - memcpy(&pc->addrs.netmask, &m, sizeof(m)); - memcpy(&pc->addrs.dns1, &ns1, sizeof(ns1)); - memcpy(&pc->addrs.dns2, &ns2, sizeof(ns2)); - } - return st; + ppp_control *pc = &ppp_controls[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->open) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o)); + SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h)); + SMEMCPY(&pc->addrs.netmask, &m, sizeof(m)); + SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1)); + SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2)); + } + return st; } -/* +/** * cifaddr - Clear the interface IP addresses, and delete routes * through the interface if possible. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h IP broadcast address ??? */ -int cifaddr( - int pd, /* Interface unit ??? */ - u32_t o, /* Our IP address ??? */ - u32_t h /* IP broadcast address ??? */ -) +int +cifaddr( int pd, u32_t o, u32_t h) { - PPPControl *pc = &pppControl[pd]; - int st = 1; - - (void)o; - (void)h; - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { - st = 0; - PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); - } else { - IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0); - IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0); - IP4_ADDR(&pc->addrs.netmask, 255,255,255,0); - IP4_ADDR(&pc->addrs.dns1, 0,0,0,0); - IP4_ADDR(&pc->addrs.dns2, 0,0,0,0); - } - return st; + ppp_control *pc = &ppp_controls[pd]; + int st = 1; + + LWIP_UNUSED_ARG(o); + LWIP_UNUSED_ARG(h); + if (pd < 0 || pd >= NUM_PPP || !pc->open) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.netmask, 255,255,255,0); + IP4_ADDR(&pc->addrs.dns1, 0,0,0,0); + IP4_ADDR(&pc->addrs.dns2, 0,0,0,0); + } + return st; } /* * sifdefaultroute - assign a default route through the address given. */ -int sifdefaultroute(int pd, u32_t l, u32_t g) +int +sifdefaultroute(int pd, u32_t l, u32_t g) { - PPPControl *pc = &pppControl[pd]; - int st = 1; - - (void)l; - (void)g; - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { - st = 0; - PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); - } else { - netif_set_default(&pc->netif); - } + ppp_control *pc = &ppp_controls[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); - /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */ + if (pd < 0 || pd >= NUM_PPP || !pc->open) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(&pc->netif); + } - return st; + /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */ + + return st; } /* * cifdefaultroute - delete a default route through the address given. */ -int cifdefaultroute(int pd, u32_t l, u32_t g) +int +cifdefaultroute(int pd, u32_t l, u32_t g) { - PPPControl *pc = &pppControl[pd]; - int st = 1; - - (void)l; - (void)g; - if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { - st = 0; - PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); - } else { - netif_set_default(NULL); - } + ppp_control *pc = &ppp_controls[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); - return st; -} + if (pd < 0 || pd >= NUM_PPP || !pc->open) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(NULL); + } -void -pppMainWakeup(int pd) -{ - PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d\n", pd)); - sio_read_abort(pppControl[pd].fd); + return st; } -/* these callbacks are necessary because lcp_* functions - must be called in the same context as pppInput(), - namely the tcpip_thread(), essentially because - they manipulate timeouts which are thread-private -*/ - -static void -pppStartCB(void *arg) -{ - int pd = (int)arg; - - PPPDEBUG((LOG_DEBUG, "pppStartCB: unit %d\n", pd)); - lcp_lowerup(pd); - lcp_open(pd); /* Start protocol */ -} - -static void -pppStopCB(void *arg) -{ - int pd = (int)arg; - - PPPDEBUG((LOG_DEBUG, "pppStopCB: unit %d\n", pd)); - lcp_close(pd, "User request"); -} - -static void -pppHupCB(void *arg) -{ - int pd = (int)arg; - - PPPDEBUG((LOG_DEBUG, "pppHupCB: unit %d\n", pd)); - lcp_lowerdown(pd); - link_terminated(pd); -} /**********************************/ /*** LOCAL FUNCTION DEFINITIONS ***/ /**********************************/ + +#if PPPOS_SUPPORT + /* The main PPP process function. This implements the state machine according * to section 4 of RFC 1661: The Point-To-Point Protocol. */ -static void pppMain(void *arg) +static void ppp_loop(void *arg) { - int pd = (int)arg; - struct pbuf *p; - PPPControl* pc; + int pd = (int) arg; + + while (1) { + ppp_poll(pd); + } +} + - pc = &pppControl[pd]; +static void +ppp_main(void *arg) +{ + int pd = (int)arg; + struct pbuf *p; + ppp_control* pc; + int c; - p = pbuf_alloc(PBUF_RAW, PPP_MRU+PPP_HDRLEN, PBUF_RAM); - if(!p) { - LWIP_ASSERT("p != NULL", p); - pc->errCode = PPPERR_ALLOC; - goto out; - } + pc = &ppp_controls[pd]; + + p = pbuf_alloc(PBUF_RAW, PPP_MRU+PPP_HDRLEN, PBUF_RAM); + if (!p) { + LWIP_ASSERT("p != NULL", p); + pc->err = PPPERR_ALLOC; + goto out; + } - /* - * Start the connection and handle incoming events (packet or timeout). - */ - PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); - tcpip_callback(pppStartCB, arg); - while (lcp_phase[pd] != PHASE_DEAD) { - if (pc->kill_link) { - PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d kill_link -> pppStopCB\n", pd)); - pc->errCode = PPPERR_USER; - /* This will leave us at PHASE_DEAD. */ - tcpip_callback(pppStopCB, arg); - pc->kill_link = 0; - } - else if (pc->sig_hup) { - PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d sig_hup -> pppHupCB\n", pd)); - pc->sig_hup = 0; - tcpip_callback(pppHupCB, arg); - } else { - int c = sio_read(pc->fd, p->payload, p->len); - if(c > 0) { - pppInProc(pd, p->payload, c); - } else { - PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d sio_read len=%d returned %d\n", pd, p->len, c)); - sys_msleep(1); /* give other tasks a chance to run */ - } - } + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); + execute_callback(ppp_start_cb, arg); + while (lcp_phase[pd] != PHASE_DEAD) { + if (pc->kill_link) { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d kill_link -> ppp_stop_cb\n", pd)); + pc->err = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + execute_callback(ppp_stop_cb, arg); + pc->kill_link = 0; + } else if (pc->sig_hup) { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d sig_hup -> ppp_hup_cb\n", pd)); + pc->sig_hup = 0; + execute_callback(ppp_hup_cb, arg); + } else { + c = sio_read(pc->fd, p->payload, p->len); + if(c > 0) { + ppp_in_proc(pd, p->payload, c); + } else { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d sio_read len=%d returned %d\n", pd, p->len, c)); + sys_msleep(1); /* give other tasks a chance to run */ + } } - PPPDEBUG((LOG_INFO, "pppMain: unit %d: PHASE_DEAD\n", pd)); - pbuf_free(p); + } + PPPDEBUG((LOG_INFO, "pppMain: unit %d: PHASE_DEAD\n", pd)); + ppp_drop(pc); /* bug fix #17726 */ + pbuf_free(p); out: - PPPDEBUG((LOG_DEBUG, "pppMain: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); - if(pc->linkStatusCB) - pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d: status_cb=%lx err=%d\n", pd, pc->status_cb, pc->err)); + if(pc->status_cb) { + pc->status_cb(pc->status_ctx, pc->err ? pc->err : PPPERR_PROTOCOL, NULL); + } + + pc->open = 0; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT - pc->openFlag = 0; +void +pppOverEthernetInitFailed(void* arg) +{ + ppp_control* pc; + int pd = (int)arg; + + ppp_hup_cb(arg); + ppp_stop_cb(arg); + + pc = &ppp_controls[pd]; + pppoe_destroy(&pc->netif); + pc->open = 0; + + if(pc->status_cb) { + pc->status_cb(pc->status_ctx, pc->err ? pc->err : PPPERR_PROTOCOL, NULL); + } } -static struct pbuf *pppSingleBuf(struct pbuf *p) +static void +pppOverEthernetLinkStatusCB(int pd, int up) { - struct pbuf *q, *b; - u_char *pl; + if(up) { + PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); + execute_callback(ppp_start_cb, (void*)pd); + } else { + ppp_control* pc; + pc = &ppp_controls[pd]; + execute_callback(pppOverEthernetInitFailed, (void*)pd); + } +} +#endif /* PPPOE_SUPPORT */ - if(p->tot_len == p->len) - return p; +struct pbuf * +ppp_single_buf(struct pbuf *p) +{ + struct pbuf *q, *b; + u8_t *pl; - q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); - if(!q) { - PPPDEBUG((LOG_ERR, - "pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len)); - return p; /* live dangerously */ - } + if(p->tot_len == p->len) { + return p; + } - for(b = p, pl = q->payload; b != NULL; b = b->next) { - memcpy(pl, b->payload, b->len); - pl += b->len; - } + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG((LOG_ERR, + "ppp_single_buf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } - pbuf_free(p); + for(b = p, pl = q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } - return q; + pbuf_free(p); + + return q; } -struct pppInputHeader { - int unit; - u16_t proto; +struct ppp_inputHeader { + int unit; + u16_t proto; }; /* * Pass the processed input packet to the appropriate handler. * This function and all handlers run in the context of the tcpip_thread */ -static void pppInput(void *arg) +static void +ppp_input(void *arg) { - struct pbuf *nb = (struct pbuf *)arg; - u16_t protocol; - int pd; + struct pbuf *nb = (struct pbuf *)arg; + u16_t protocol; + int pd; - pd = ((struct pppInputHeader *)nb->payload)->unit; - protocol = ((struct pppInputHeader *)nb->payload)->proto; + pd = ((struct ppp_inputHeader *)nb->payload)->unit; + protocol = ((struct ppp_inputHeader *)nb->payload)->proto; - pbuf_header(nb, -(int)sizeof(struct pppInputHeader)); + if(pbuf_header(nb, -(int)sizeof(struct ppp_inputHeader))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } -#if LINK_STATS - lwip_stats.link.recv++; -#endif /* LINK_STATS */ + LINK_STATS_INC(link.recv); - /* - * Toss all non-LCP packets unless LCP is OPEN. - * Until we get past the authentication phase, toss all packets - * except LCP, LQR and authentication packets. - */ - if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) { - if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) || - (lcp_phase[pd] != PHASE_AUTHENTICATE)) { - PPPDEBUG((LOG_INFO, "pppInput: discarding proto 0x%04X in phase %d\n", protocol, lcp_phase[pd])); - goto drop; - } + /* + * Toss all non-LCP packets unless LCP is OPEN. + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) { + if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) || + (lcp_phase[pd] != PHASE_AUTHENTICATE)) { + PPPDEBUG((LOG_INFO, "ppp_input: discarding proto 0x%04X in phase %d\n", protocol, lcp_phase[pd])); + goto drop; } + } - switch(protocol) { + switch(protocol) { case PPP_VJC_COMP: /* VJ compressed TCP */ -#if VJ_SUPPORT > 0 - PPPDEBUG((LOG_INFO, "pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len)); - /* - * Clip off the VJ header and prepend the rebuilt TCP/IP header and - * pass the result to IP. - */ - if (vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) { - pppControl[pd].netif.input(nb, &pppControl[pd].netif); - return; - } - /* Something's wrong so drop it. */ - PPPDEBUG((LOG_WARNING, "pppInput[%d]: Dropping VJ compressed\n", pd)); -#else - /* No handler for this protocol so drop the packet. */ - PPPDEBUG((LOG_INFO, "pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload)); -#endif /* VJ_SUPPORT > 0 */ - break; +#if VJ_SUPPORT + PPPDEBUG((LOG_INFO, "ppp_input[%d]: vj_comp in pbuf len=%d\n", pd, nb->len)); + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + if ((vj_uncompress_tcp(&nb, &ppp_controls[pd].vj_comp) >= 0) && (ppp_controls[pd].netif.input)) { + ppp_controls[pd].netif.input(nb, &ppp_controls[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG((LOG_WARNING, "ppp_input[%d]: Dropping VJ compressed\n", pd)); +#else /* VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG((LOG_INFO, "ppp_input[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload)); +#endif /* VJ_SUPPORT */ + break; + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ -#if VJ_SUPPORT > 0 - PPPDEBUG((LOG_INFO, "pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len)); - /* - * Process the TCP/IP header for VJ header compression and then pass - * the packet to IP. - */ - if (vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) { - pppControl[pd].netif.input(nb, &pppControl[pd].netif); - return; - } - /* Something's wrong so drop it. */ - PPPDEBUG((LOG_WARNING, "pppInput[%d]: Dropping VJ uncompressed\n", pd)); -#else - /* No handler for this protocol so drop the packet. */ - PPPDEBUG((LOG_INFO, - "pppInput[%d]: drop VJ UnComp in %d:.*H\n", - pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload)); -#endif /* VJ_SUPPORT > 0 */ - break; +#if VJ_SUPPORT + PPPDEBUG((LOG_INFO, "ppp_input[%d]: vj_un in pbuf len=%d\n", pd, nb->len)); + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + if ((vj_uncompress_uncomp(nb, &ppp_controls[pd].vj_comp) >= 0) && ppp_controls[pd].netif.input) { + ppp_controls[pd].netif.input(nb, &ppp_controls[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG((LOG_WARNING, "ppp_input[%d]: Dropping VJ uncompressed\n", pd)); +#else /* VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG((LOG_INFO, + "ppp_input[%d]: drop VJ UnComp in %d:.*H\n", + pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload)); +#endif /* VJ_SUPPORT */ + break; + case PPP_IP: /* Internet Protocol */ - PPPDEBUG((LOG_INFO, "pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len)); - pppControl[pd].netif.input(nb, &pppControl[pd].netif); - return; - default: - { - struct protent *protp; - int i; + PPPDEBUG((LOG_INFO, "ppp_input[%d]: ip in pbuf len=%d\n", pd, nb->len)); + if (ppp_controls[pd].netif.input) { + ppp_controls[pd].netif.input(nb, &ppp_controls[pd].netif); + return; + } + break; + + default: { + struct protent *protp; + int i; - /* - * Upcall the proper protocol input routine. - */ - for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { - if (protp->protocol == protocol && protp->enabled_flag) { - PPPDEBUG((LOG_INFO, "pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len)); - nb = pppSingleBuf(nb); - (*protp->input)(pd, nb->payload, nb->len); - goto out; - } - } + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + PPPDEBUG((LOG_INFO, "ppp_input[%d]: %s len=%d\n", pd, protp->name, nb->len)); + nb = ppp_single_buf(nb); + (*protp->input)(pd, nb->payload, nb->len); + goto out; + } + } - /* No handler for this protocol so reject the packet. */ - PPPDEBUG((LOG_INFO, "pppInput[%d]: rejecting unsupported proto 0x%04X len=%d\n", pd, protocol, nb->len)); - pbuf_header(nb, sizeof(protocol)); + /* No handler for this protocol so reject the packet. */ + PPPDEBUG((LOG_INFO, "ppp_input[%d]: rejecting unsupported proto 0x%04X len=%d\n", pd, protocol, nb->len)); + if (pbuf_header(nb, sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } #if BYTE_ORDER == LITTLE_ENDIAN - protocol = htons(protocol); - memcpy(nb->payload, &protocol, sizeof(protocol)); -#endif - lcp_sprotrej(pd, nb->payload, nb->len); - } - break; + protocol = htons(protocol); + SMEMCPY(nb->payload, &protocol, sizeof(protocol)); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + lcp_sprotrej(pd, nb->payload, nb->len); } + break; + } drop: -#if LINK_STATS - lwip_stats.link.drop++; -#endif + LINK_STATS_INC(link.drop); out: - pbuf_free(nb); - return; + pbuf_free(nb); + return; } - +#if PPPOS_SUPPORT /* * Drop the input packet. */ -static void pppDrop(PPPControl *pc) +static void +ppp_drop(ppp_control *pc) { - if (pc->inHead != NULL) { -#if 0 - PPPDEBUG((LOG_INFO, "pppDrop: %d:%.*H\n", pc->inHead->len, min(60, pc->inHead->len * 2), pc->inHead->payload)); -#endif - PPPDEBUG((LOG_INFO, "pppDrop: pbuf len=%d\n", pc->inHead->len)); - if (pc->inTail && (pc->inTail != pc->inHead)) - pbuf_free(pc->inTail); - pbuf_free(pc->inHead); - pc->inHead = NULL; - pc->inTail = NULL; + if (pc->in_head != NULL) { +#if 0 + PPPDEBUG((LOG_INFO, "ppp_drop: %d:%.*H\n", pc->in_head->len, min(60, pc->in_head->len * 2), pc->in_head->payload)); +#endif + PPPDEBUG((LOG_INFO, "ppp_drop: pbuf len=%d\n", pc->in_head->len)); + if (pc->in_tail && (pc->in_tail != pc->in_head)) { + pbuf_free(pc->in_tail); } -#if VJ_SUPPORT > 0 - vj_uncompress_err(&pc->vjComp); -#endif + pbuf_free(pc->in_head); + pc->in_head = NULL; + pc->in_tail = NULL; + } +#if VJ_SUPPORT + vj_uncompress_err(&pc->vj_comp); +#endif /* VJ_SUPPORT */ -#if LINK_STATS - lwip_stats.link.drop++; -#endif /* LINK_STATS */ + LINK_STATS_INC(link.drop); } - -/* +/** * Process a received octet string. */ -static void pppInProc(int pd, u_char *s, int l) +static void +ppp_in_proc(int pd, u8_t *s, int l) { - PPPControl *pc = &pppControl[pd]; - struct pbuf *nextNBuf; - u_char curChar; - - PPPDEBUG((LOG_DEBUG, "pppInProc[%d]: got %d bytes\n", pd, l)); - while (l-- > 0) { - curChar = *s++; - - /* Handle special characters. */ - if (ESCAPE_P(pc->inACCM, curChar)) { - /* Check for escape sequences. */ - /* XXX Note that this does not handle an escaped 0x5d character which - * would appear as an escape character. Since this is an ASCII ']' - * and there is no reason that I know of to escape it, I won't complicate - * the code to handle this case. GLL */ - if (curChar == PPP_ESCAPE) - pc->inEscaped = 1; - /* Check for the flag character. */ - else if (curChar == PPP_FLAG) { - /* If this is just an extra flag character, ignore it. */ - if (pc->inState <= PDADDRESS) - ; - /* If we haven't received the packet header, drop what has come in. */ - else if (pc->inState < PDDATA) { - PPPDEBUG((LOG_WARNING, - "pppInProc[%d]: Dropping incomplete packet %d\n", - pd, pc->inState)); -#if LINK_STATS - lwip_stats.link.lenerr++; -#endif - pppDrop(pc); - } - /* If the fcs is invalid, drop the packet. */ - else if (pc->inFCS != PPP_GOODFCS) { - PPPDEBUG((LOG_INFO, - "pppInProc[%d]: Dropping bad fcs 0x%04X proto=0x%04X\n", - pd, pc->inFCS, pc->inProtocol)); -#if LINK_STATS - lwip_stats.link.chkerr++; + ppp_control *pc = &ppp_controls[pd]; + struct pbuf *nextNBuf; + u8_t curChar; + +#if RECORD_SUPPORT + record_in(s, l); #endif - pppDrop(pc); - } - /* Otherwise it's a good packet so pass it on. */ - else { - - /* Trim off the checksum. */ - if(pc->inTail->len >= 2) { - pc->inTail->len -= 2; + + PPPDEBUG((LOG_DEBUG, "ppp_in_proc[%d]: got %d bytes\n", pd, l)); + while (l-- > 0) { + curChar = *s++; - pc->inTail->tot_len = pc->inTail->len; - if (pc->inTail != pc->inHead) { - pbuf_cat(pc->inHead, pc->inTail); - } - } else { - pc->inTail->tot_len = pc->inTail->len; - if (pc->inTail != pc->inHead) { - pbuf_cat(pc->inHead, pc->inTail); - } + /* Handle special characters. */ + if (ESCAPE_P(pc->in_accm, curChar)) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (curChar == PPP_ESCAPE) { + pc->in_escaped = 1; + /* Check for the flag character. */ + } else if (curChar == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pc->in_state <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pc->in_state < PDDATA) { + PPPDEBUG((LOG_WARNING, + "ppp_in_proc[%d]: Dropping incomplete packet %d\n", + pd, pc->in_state)); + LINK_STATS_INC(link.lenerr); + ppp_drop(pc); + /* If the fcs is invalid, drop the packet. */ + } else if (pc->in_fcs != PPP_GOODFCS) { + PPPDEBUG((LOG_INFO, + "ppp_in_proc[%d]: Dropping bad fcs 0x%04X proto=0x%04X\n", + pd, pc->in_fcs, pc->in_proto)); + LINK_STATS_INC(link.chkerr); + ppp_drop(pc); + /* Otherwise it's a good packet so pass it on. */ + } else { + /* Trim off the checksum. */ + if(pc->in_tail->len >= 2) { + pc->in_tail->len -= 2; - pbuf_realloc(pc->inHead, pc->inHead->tot_len - 2); - } + pc->in_tail->tot_len = pc->in_tail->len; + if (pc->in_tail != pc->in_head) { + pbuf_cat(pc->in_head, pc->in_tail); + } + } else { + pc->in_tail->tot_len = pc->in_tail->len; + if (pc->in_tail != pc->in_head) { + pbuf_cat(pc->in_head, pc->in_tail); + } + + pbuf_realloc(pc->in_head, pc->in_head->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + if (execute_callback(ppp_input, pc->in_head) != ERR_OK) { + PPPDEBUG((LOG_ERR, "ppp_in_proc[%d]: execute_callback() failed, dropping packet\n", pd)); + pbuf_free(pc->in_head); + LINK_STATS_INC(link.drop); + } + pc->in_head = NULL; + pc->in_tail = NULL; + } - /* Dispatch the packet thereby consuming it. */ - if(tcpip_callback(pppInput, pc->inHead) != ERR_OK) { - PPPDEBUG((LOG_ERR, - "pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pd)); - pbuf_free(pc->inHead); -#if LINK_STATS - lwip_stats.link.drop++; + /* Prepare for a new packet. */ + pc->in_fcs = PPP_INITFCS; + pc->in_state = PDADDRESS; + pc->in_escaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG((LOG_WARNING, + "ppp_in_proc[%d]: Dropping ACCM char <%d>\n", pd, curChar)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pc->in_escaped) { + pc->in_escaped = 0; + curChar ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pc->in_state) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (curChar != PPP_ALLSTATIONS) { + break; + } + + /* Fall through */ + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pc->in_fcs = PPP_INITFCS; + + /* Fall through */ + case PDADDRESS: /* Process address field. */ + if (curChar == PPP_ALLSTATIONS) { + pc->in_state = PDCONTROL; + break; + } + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (curChar == PPP_UI) { + pc->in_state = PDPROTOCOL1; + break; + } +#if 0 + else { + PPPDEBUG((LOG_WARNING, + "ppp_in_proc[%d]: Invalid control <%d>\n", pd, curChar)); + pc->in_state = PDSTART; + } #endif - } - pc->inHead = NULL; - pc->inTail = NULL; - } - - /* Prepare for a new packet. */ - pc->inFCS = PPP_INITFCS; - pc->inState = PDADDRESS; - pc->inEscaped = 0; - } - /* Other characters are usually control characters that may have - * been inserted by the physical layer so here we just drop them. */ - else { - PPPDEBUG((LOG_WARNING, - "pppInProc[%d]: Dropping ACCM char <%d>\n", pd, curChar)); - } - } - /* Process other characters. */ - else { - /* Unencode escaped characters. */ - if (pc->inEscaped) { - pc->inEscaped = 0; - curChar ^= PPP_TRANS; + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (curChar & 1) { + pc->in_proto = curChar; + pc->in_state = PDDATA; + } else { + pc->in_proto = (u32_t)curChar << 8; + pc->in_state = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pc->in_proto |= curChar; + pc->in_state = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pc->in_tail == NULL || pc->in_tail->len == PBUF_POOL_BUFSIZE) { + if(pc->in_tail) { + pc->in_tail->tot_len = pc->in_tail->len; + if (pc->in_tail != pc->in_head) { + pbuf_cat(pc->in_head, pc->in_tail); + } } - - /* Process character relative to current state. */ - switch(pc->inState) { - case PDIDLE: /* Idle state - waiting. */ - /* Drop the character if it's not 0xff - * we would have processed a flag character above. */ - if (curChar != PPP_ALLSTATIONS) { - break; - } + /* If we haven't started a packet, we need a packet header. */ + nextNBuf = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL); + if (nextNBuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG((LOG_ERR, "ppp_in_proc[%d]: NO FREE MBUFS!\n", pd)); + LINK_STATS_INC(link.memerr); + ppp_drop(pc); + pc->in_state = PDSTART; /* Wait for flag sequence. */ + break; + } + nextNBuf->len = 0; + if (pc->in_head == NULL) { + struct ppp_inputHeader *pih = nextNBuf->payload; + + pih->unit = pd; + pih->proto = pc->in_proto; - /* Fall through */ - case PDSTART: /* Process start flag. */ - /* Prepare for a new packet. */ - pc->inFCS = PPP_INITFCS; + nextNBuf->len += sizeof(*pih); + + pc->in_head = nextNBuf; + } + pc->in_tail = nextNBuf; + } + /* Load character into buffer. */ + ((u8_t*)pc->in_tail->payload)[pc->in_tail->len++] = curChar; + break; + } + + /* update the frame check sequence number. */ + pc->in_fcs = PPP_FCS(pc->in_fcs, curChar); + } + } + + randm_randomize(); +} +#endif /* PPPOS_SUPPORT */ - /* Fall through */ - case PDADDRESS: /* Process address field. */ - if (curChar == PPP_ALLSTATIONS) { - pc->inState = PDCONTROL; - break; - } - /* Else assume compressed address and control fields so - * fall through to get the protocol... */ - case PDCONTROL: /* Process control field. */ - /* If we don't get a valid control code, restart. */ - if (curChar == PPP_UI) { - pc->inState = PDPROTOCOL1; - break; - } -#if 0 - else { - PPPDEBUG((LOG_WARNING, - "pppInProc[%d]: Invalid control <%d>\n", pd, curChar)); - pc->inState = PDSTART; - } -#endif - case PDPROTOCOL1: /* Process protocol field 1. */ - /* If the lower bit is set, this is the end of the protocol - * field. */ - if (curChar & 1) { - pc->inProtocol = curChar; - pc->inState = PDDATA; - } - else { - pc->inProtocol = (u_int)curChar << 8; - pc->inState = PDPROTOCOL2; - } - break; - case PDPROTOCOL2: /* Process protocol field 2. */ - pc->inProtocol |= curChar; - pc->inState = PDDATA; - break; - case PDDATA: /* Process data byte. */ - /* Make space to receive processed data. */ - if (pc->inTail == NULL || pc->inTail->len == PBUF_POOL_BUFSIZE) { - if(pc->inTail) { - pc->inTail->tot_len = pc->inTail->len; - if (pc->inTail != pc->inHead) { - pbuf_cat(pc->inHead, pc->inTail); - } - } - /* If we haven't started a packet, we need a packet header. */ - nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); - if (nextNBuf == NULL) { - /* No free buffers. Drop the input packet and let the - * higher layers deal with it. Continue processing - * the received pbuf chain in case a new packet starts. */ - PPPDEBUG((LOG_ERR, "pppInProc[%d]: NO FREE MBUFS!\n", pd)); -#if LINK_STATS - lwip_stats.link.memerr++; -#endif /* LINK_STATS */ - pppDrop(pc); - pc->inState = PDSTART; /* Wait for flag sequence. */ - break; - } - if (pc->inHead == NULL) { - struct pppInputHeader *pih = nextNBuf->payload; +#if PPPOE_SUPPORT +void +ppp_in_proc_over_ethernet(int pd, struct pbuf *pb) +{ + struct ppp_inputHeader *pih; + u16_t in_proto; + + if(pb->len < sizeof(in_proto)) { + PPPDEBUG((LOG_ERR, "ppp_in_proc_over_ethernet: too small for protocol field\n")); + goto drop; + } + + in_proto = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + + /* make room for ppp_inputHeader - should not fail */ + if (pbuf_header(pb, sizeof(*pih) - sizeof(in_proto)) != 0) { + PPPDEBUG((LOG_ERR, "ppp_in_proc_over_ethernet: could not allocate room for header\n")); + goto drop; + } - pih->unit = pd; - pih->proto = pc->inProtocol; + pih = pb->payload; - nextNBuf->len += sizeof(*pih); + pih->unit = pd; + pih->proto = in_proto; - pc->inHead = nextNBuf; - } - pc->inTail = nextNBuf; - } - /* Load character into buffer. */ - ((u_char*)pc->inTail->payload)[pc->inTail->len++] = curChar; - break; - } + /* Dispatch the packet thereby consuming it. */ + if(execute_callback(ppp_input, pb) != ERR_OK) { + PPPDEBUG((LOG_ERR, "ppp_in_proc_over_ethernet[%d]: execute_callback() failed, dropping packet\n", pd)); + goto drop; + } - /* update the frame check sequence number. */ - pc->inFCS = PPP_FCS(pc->inFCS, curChar); - } - } - avRandomize(); + return; + +drop: + LINK_STATS_INC(link.drop); + pbuf_free(pb); + return; } +#endif /* PPPOE_SUPPORT */ #endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/ppp_oe.c b/packages/net/lwip_tcpip/current/src/netif/ppp/ppp_oe.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/ppp_oe.c @@ -0,0 +1,1227 @@ +/***************************************************************************** +* ppp_oe.c - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE 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 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. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" + +#include "lwip/sys.h" + +#include "netif/ppp_oe.h" +#include "netif/etharp.h" + +#include +#include + +/** @todo Replace this part with a simple list like other lwIP lists */ +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ + + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (VAL) / 256; \ + *(PTR)++ = (VAL) % 256 + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +struct pppoe_softc { + LIST_ENTRY(pppoe_softc) sc_list; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ + u8_t *sc_ac_cookie; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + +/* input routines */ +static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *); + +/* management routines */ +static int pppoe_do_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u32_t, const u8_t *); + +/* internal helper functions */ +static struct pppoe_softc * pppoe_find_softc_by_session(u32_t, struct netif *); +static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *); + +static LIST_HEAD(pppoe_softc_head, pppoe_softc) pppoe_softc_list; + +int pppoe_hdrlen; + +void +pppoe_init(void) +{ + pppoe_hdrlen = sizeof(struct eth_hdr) + PPPOE_HEADERLEN; + LIST_INIT(&pppoe_softc_list); +} + +err_t +pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = mem_malloc(sizeof(struct pppoe_softc)); + if(!sc) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->sc_pd = pd; + sc->sc_linkStatusCB = linkStatusCB; + sc->sc_ethif = ethif; + + LIST_INSERT_HEAD(&pppoe_softc_list, sc, sc_list); + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct netif *ifp) +{ + struct pppoe_softc * sc; + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif == ifp) { + break; + } + } + + if(!(sc && (sc->sc_ethif == ifp))) { + return ERR_IF; + } + + tcpip_untimeout(pppoe_timeout, sc); + LIST_REMOVE(sc, sc_list); + + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + } + mem_free(sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc * +pppoe_find_softc_by_session(u32_t session, struct netif *rcvif) +{ + struct pppoe_softc *sc; + + if (session == 0) { + return NULL; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_ethif == rcvif) { + return sc; + } else { + return NULL; + } + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc * +pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) +{ + struct pppoe_softc *sc, *t; + + if (LIST_EMPTY(&pppoe_softc_list)) { + return NULL; + } + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG((LOG_DEBUG, "pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state); + return NULL; + } + if (sc->sc_ethif != rcvif) { + printf("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + return NULL; + } + return sc; +} + +static void +pppoe_linkstatus_up(void *arg) +{ + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + sc->sc_linkStatusCB(sc->sc_pd, 1); +} + +/* analyze and handle a single received packet while not in session state */ +static void +pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + char *error; + u8_t *ac_cookie; + size_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off = 0, err, errortag; + struct eth_hdr *ethhdr; + + pb = pppSingleBuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + errortag = 0; + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off += sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off <= PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", pb->len); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + if (sc != NULL) { + snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + error = NULL; + if (errortag && len) { + error = mem_malloc(len+1); + if (error) { + strncpy(error, (char*)pb->payload + off + sizeof(pt), len); + error[len-1] = '\0'; + } + } + if (error) { + printf("%s: %s: %s\n", devname, err_msg, error); + mem_free(error); + } else { + printf("%s: %s\n", devname, err_msg); + } + if (errortag) { + goto done; + } + } + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* printf("pppoe: free passive interface is not found\n"); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; + #endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: + #ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + printf("pppoe: received PADR but not includes ac_cookie\n"); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADR but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* notify upper layers */ + break; + #else + /* ignore, we are no access concentrator */ + goto done; + #endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADO but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (ac_cookie) { + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + } + sc->sc_ac_cookie = mem_malloc(ac_cookie_len); + if (sc->sc_ac_cookie == NULL) { + goto done; + } + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + tcpip_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + tcpip_untimeout(pppoe_timeout, sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) { + goto done; + } + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + printf("%c%c%"U16_F": unknown code (0x%04x) session = 0x%04x\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + ph->code, session); + } else { + printf("pppoe: unknown code (0x%04x) session = 0x%04x\n", ph->code, session); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_disc_input(struct netif *netif, struct pbuf *p) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + pppoe_dispatch_disc_pkt(netif, p); + } else { + pbuf_free(p); + } +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = pppSingleBuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + printf("pppoe_data_input: could not get PPPoE header\n"); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe: input for unknown session 0x%x, sending PADT\n", session); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG((LOG_DEBUG, "pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->len < plen) { + goto drop; + } + + pppInProcOverEthernet(sc->sc_pd, pb); + + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len, l1 = 0, l2 = 0; /* XXX: gcc */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) { + PPPDEBUG((LOG_ERR, "ERROR: pppoe_send_padi in state %d", sc->sc_state)); + } + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ + if (sc->sc_service_name != NULL) { + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + int retry_wait, err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + sc->sc_padi_retried++; + if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_STATE_CLOSING: + pppoe_do_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + return EBUSY; + } + +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + tcpip_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) { + return; + } + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + tcpip_timeout(20, pppoe_timeout, sc); +} + +static int +pppoe_do_disconnect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state < PPPOE_STATE_SESSION) { + err = EBUSY; + } else { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + return err; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + sc->sc_state = PPPOE_STATE_CLOSING; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + /* clear connection state */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) { + return ERR_CONN; + } + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u32_t session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = htons(ETHTYPE_PPPOEDISC); + MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload + sizeof(struct eth_hdr); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 /*def PFIL_HOOKS*/ +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + printf("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + tcpip_untimeout(pppoe_timeout, sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_linkStatusCB(sc->sc_pd, 0); + + /* clean up softc */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; +} + +#endif /* PPPOE_SUPPORT */ + diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/pppdebug.h b/packages/net/lwip_tcpip/current/src/netif/ppp/pppdebug.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/pppdebug.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/pppdebug.h @@ -29,7 +29,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 98-07-29 Guy Lancaster , Global Election Systems Inc. -* Original. +* Original. * ***************************************************************************** */ @@ -41,13 +41,13 @@ ************************/ /* Trace levels. */ typedef enum { - LOG_CRITICAL = 0, - LOG_ERR = 1, - LOG_NOTICE = 2, - LOG_WARNING = 3, - LOG_INFO = 5, - LOG_DETAIL = 6, - LOG_DEBUG = 7 +LOG_CRITICAL = 0, +LOG_ERR = 1, +LOG_NOTICE = 2, +LOG_WARNING = 3, +LOG_INFO = 5, +LOG_DETAIL = 6, +LOG_DEBUG = 7 } LogCodes; @@ -55,23 +55,23 @@ typedef enum { *** PUBLIC FUNCTIONS *** ***********************/ /* - * ppp_trace - a form of printf to send tracing information to stderr + * ppp_trace - a form of printf to send tracing information to stderr */ void ppp_trace(int level, const char *format,...); -#if PPP_DEBUG > 0 +#define TRACELCP PPP_DEBUG + +#if PPP_DEBUG #define AUTHDEBUG(a) ppp_trace a #define IPCPDEBUG(a) ppp_trace a #define UPAPDEBUG(a) ppp_trace a -#define LCPDEBUG(a) ppp_trace a -#define FSMDEBUG(a) ppp_trace a +#define LCPDEBUG(a) ppp_trace a +#define FSMDEBUG(a) ppp_trace a #define CHAPDEBUG(a) ppp_trace a -#define PPPDEBUG(a) ppp_trace a +#define PPPDEBUG(a) ppp_trace a -#define TRACELCP 1 - -#else +#else /* PPP_DEBUG */ #define AUTHDEBUG(a) #define IPCPDEBUG(a) @@ -79,11 +79,8 @@ void ppp_trace(int level, const char *fo #define LCPDEBUG(a) #define FSMDEBUG(a) #define CHAPDEBUG(a) - #define PPPDEBUG(a) -#define TRACELCP 0 - -#endif +#endif /* PPP_DEBUG */ #endif /* PPPDEBUG_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/randm.c b/packages/net/lwip_tcpip/current/src/netif/ppp/randm.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/randm.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/randm.c @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -31,27 +31,32 @@ * Extracted from avos. *****************************************************************************/ -#include "ppp.h" -#if PPP_SUPPORT > 0 +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + #include "md5.h" #include "randm.h" +#include "timesys.h" +#include "netif/ppp/ppp.h" #include "pppdebug.h" +#if MD5_SUPPORT /* this module depends on MD5 */ -#if MD5_SUPPORT>0 /* this module depends on MD5 */ -#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */ +#define RAND_POOL_SIZE 16 /* Bytes stored in the pool of randomness. */ /*****************************/ /*** LOCAL DATA STRUCTURES ***/ /*****************************/ -static char randPool[RANDPOOLSZ]; /* Pool of randomness. */ -static long randCount = 0; /* Pseudo-random incrementer */ +static char rand_pool[RAND_POOL_SIZE]; /* Pool of randomness. */ +static long rand_count = 0; /* Pseudo-random incrementer */ /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ /***********************************/ + /* * Initialize the random number generator. * @@ -60,9 +65,10 @@ static long randCount = 0; /* Pseud * real-time clock. We'll accumulate more randomness as soon * as things start happening. */ -void avRandomInit() +void +randm_init() { - avChurnRand(NULL, 0); + randm_churn(NULL, 0); } /* @@ -75,33 +81,32 @@ void avRandomInit() * * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 */ -void avChurnRand(char *randData, u32_t randLen) +void +randm_churn(char *data, u32_t len) { - MD5_CTX md5; + md5_ctx md5; -/* ppp_trace(LOG_INFO, "churnRand: %u@%P\n", randLen, randData); */ - MD5Init(&md5); - MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); - if (randData) - MD5Update(&md5, (u_char *)randData, randLen); - else { - struct { - /* INCLUDE fields for any system sources of randomness */ - char foobar; - } sysData; - - /* Load sysData fields here. */ - ; - MD5Update(&md5, (u_char *)&sysData, sizeof(sysData)); - } - MD5Final((u_char *)randPool, &md5); + /* ppp_trace(LOG_INFO, "churnRand: %u@%P\n", len, data); */ + md5_init(&md5); + md5_update(&md5, (u8_t *) rand_pool, sizeof(rand_pool)); + if (data) { + md5_update(&md5, (u8_t *) data, len); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + char foobar; + } sys_data; + /* Load sys_data fields here. */ + md5_update(&md5, (u8_t *) &sys_data, sizeof(sys_data)); + } + md5_final((u8_t *) rand_pool, &md5); /* ppp_trace(LOG_INFO, "churnRand: -> 0\n"); */ } /* * Use the random pool to generate random data. This degrades to pseudo * random when used faster than randomness is supplied using churnRand(). - * Note: It's important that there be sufficient randomness in randPool + * Note: It's important that there be sufficient randomness in rand_pool * before this is called for otherwise the range of the result may be * narrow enough to make a search feasible. * @@ -111,53 +116,55 @@ void avChurnRand(char *randData, u32_t r * so that you don't ever publish the seed which could possibly help * predict future values. * XXX Why don't we preserve md5 between blocks and just update it with - * randCount each time? Probably there is a weakness but I wish that + * rand_count each time? Probably there is a weakness but I wish that * it was documented. */ -void avGenRand(char *buf, u32_t bufLen) +void +randm_gen_rand(char *buf, u32_t len) { - MD5_CTX md5; - u_char tmp[16]; - u32_t n; + md5_ctx md5; + u8_t tmp[16]; + u32_t n; - while (bufLen > 0) { - n = LWIP_MIN(bufLen, RANDPOOLSZ); - MD5Init(&md5); - MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); - MD5Update(&md5, (u_char *)&randCount, sizeof(randCount)); - MD5Final(tmp, &md5); - randCount++; - memcpy(buf, tmp, n); - buf += n; - bufLen -= n; - } + while (len > 0) { + n = LWIP_MIN(len, RAND_POOL_SIZE); + md5_init(&md5); + md5_update(&md5, (u8_t *) rand_pool, sizeof(rand_pool)); + md5_update(&md5, (u8_t *) &rand_count, sizeof(rand_count)); + md5_final(tmp, &md5); + rand_count++; + MEMCPY(buf, tmp, n); + buf += n; + len -= n; + } } /* * Return a new random number. */ -u32_t avRandom() +u32_t +randm_rand() { - u32_t newRand; + u32_t rand; - avGenRand((char *)&newRand, sizeof(newRand)); + randm_gen_rand((char *) &rand, sizeof(rand)); - return newRand; + return rand; } #else /* MD5_SUPPORT */ - /*****************************/ /*** LOCAL DATA STRUCTURES ***/ /*****************************/ -static int avRandomized = 0; /* Set when truely randomized. */ -static u32_t avRandomSeed = 0; /* Seed used for random number generation. */ +static int randomized = 0; /* Set when truely randomized. */ +static u32_t seed = 0; /* Seed used for random number generation. */ /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ /***********************************/ + /* * Initialize the random number generator. * @@ -172,31 +179,14 @@ static u32_t avRandomSeed = 0; /* S * operational. Thus we call it again on the first random * event. */ -void avRandomInit() +void +randm_init() { -#if 0 - /* Get a pointer into the last 4 bytes of clockBuf. */ - u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]); + /* XXX week but probably enough */ + seed += ppp_jiffies(); - /* - * Initialize our seed using the real-time clock, the idle - * counter, the millisecond timer, and the hardware timer - * tick counter. The real-time clock and the hardware - * tick counter are the best sources of randomness but - * since the tick counter is only 16 bit (and truncated - * at that), the idle counter and millisecond timer - * (which may be small values) are added to help - * randomize the lower 16 bits of the seed. - */ - readClk(); - avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr - + ppp_mtime() + ((u32_t)TM1 << 16) + TM1; -#else - avRandomSeed += sys_jiffies(); /* XXX */ -#endif - - /* Initialize the Borland random number generator. */ - srand((unsigned)avRandomSeed); + /* Initialize the Borland random number generator. */ + srand(seed); } /* @@ -206,37 +196,37 @@ void avRandomInit() * value but we use the previous value to randomize the other 16 * bits. */ -void avRandomize(void) +void +randm_randomize(void) { - static u32_t last_jiffies; + static u32_t last_jiffies; - if (!avRandomized) { - avRandomized = !0; - avRandomInit(); - /* The initialization function also updates the seed. */ - } else { -/* avRandomSeed += (avRandomSeed << 16) + TM1; */ - avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */ - } - last_jiffies = sys_jiffies(); + if (!randomized) { + randomized = 1; + randm_init(); + /* The initialization function also updates the seed. */ + } else { + /* XXX week but probably enough */ + seed += (seed << 16) + (ppp_jiffies() - last_jiffies); /* XXX */ + } + last_jiffies = ppp_jiffies(); } /* * Return a new random number. * Here we use the Borland rand() function to supply a pseudo random * number which we make truely random by combining it with our own - * seed which is randomized by truely random events. + * seed which is randomized by truely random events. * Thus the numbers will be truely random unless there have been no * operator or network events in which case it will be pseudo random * seeded by the real time clock. */ -u32_t avRandom() +u32_t +randm_rand() { - return ((((u32_t)rand() << 16) + rand()) + avRandomSeed); + return ((((u32_t) rand() << 16) + rand()) + seed); } - +#endif /* MD5_SUPPORT */ -#endif /* MD5_SUPPORT */ #endif /* PPP_SUPPORT */ - diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/randm.h b/packages/net/lwip_tcpip/current/src/netif/ppp/randm.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/randm.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/randm.h @@ -7,13 +7,13 @@ * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this -* notice and the following disclaimer are included verbatim in any +* notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE 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. +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE 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, @@ -28,7 +28,7 @@ * 03-01-01 Marc Boucher * Ported to lwIP. * 98-05-29 Guy Lancaster , Global Election Systems Inc. -* Extracted from avos. +* Extracted from avos. *****************************************************************************/ #ifndef RANDM_H @@ -37,45 +37,45 @@ /*********************** *** PUBLIC FUNCTIONS *** ***********************/ + /* * Initialize the random number generator. */ -void avRandomInit(void); +void randm_init(void); /* * Churn the randomness pool on a random event. Call this early and often - * on random and semi-random system events to build randomness in time for - * usage. For randomly timed events, pass a null pointer and a zero length - * and this will use the system timer and other sources to add randomness. - * If new random data is available, pass a pointer to that and it will be - * included. + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. */ -void avChurnRand(char *randData, u32_t randLen); +void randm_churn(char *data, u32_t len); /* * Randomize our random seed value. To be called for truely random events * such as user operations and network traffic. */ #if MD5_SUPPORT -#define avRandomize() avChurnRand(NULL, 0) -#else -void avRandomize(void); -#endif +#define randm_randomize() randm_churn(NULL, 0) +#else /* MD5_SUPPORT */ +void randm_randomize(void); +#endif /* MD5_SUPPORT */ /* * Use the random pool to generate random data. This degrades to pseudo - * random when used faster than randomness is supplied using churnRand(). - * Thus it's important to make sure that the results of this are not - * published directly because one could predict the next result to at - * least some degree. Also, it's important to get a good seed before - * the first use. + * random when used faster than randomness is supplied using churnRand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. */ -void avGenRand(char *buf, u32_t bufLen); +void randm_gen_rand(char *buf, u32_t len); /* * Return a new random number. */ -u32_t avRandom(void); - +u32_t randm_rand(void); #endif /* RANDM_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/record.c b/packages/net/lwip_tcpip/current/src/netif/ppp/record.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/record.c @@ -0,0 +1,127 @@ +/***************************************************************************** +* record.c - PPP recording program file. +* +* Copyright (c) 2009 by Simon Kallweit, intefo AG +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE 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 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. +* +****************************************************************************** +* REVISION HISTORY +* +* 09-02-03 Simon Kallweit +* First implementation. +*****************************************************************************/ + +#include "lwip/opt.h" + +#if PPP_SUPPORT + +#if RECORD_SUPPORT + +#include "netif/ppp/ppp.h" +#include "record.h" + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ + +static u32_t now; +static u8_t buf[1024]; + +extern void ppp_dump_record(void *data, int len); + +/* + * write_record - Writes a single record. + */ +static void write_record(u8_t code, u8_t *data, int len) +{ + static u32_t last; + u32_t diff; + u8_t *b = buf; + + diff = now - last; + if (diff > 0) { + if (diff > 255) { + /* Absolute timestamp */ + *b++ = 0x05; + *b++ = (diff >> 24); + *b++ = (diff >> 16); + *b++ = (diff >> 8); + *b++ = diff; + } else { + /* Relative timestamp */ + *b++ = 0x06; + *b++ = diff; + } + last = now; + } + + *b++ = code; + *b++ = (len >> 8); + *b++ = len; + while (len-- && b < (buf + sizeof(buf))) + *b++ = *data++; + + ppp_dump_record(buf, b - buf); +} + +/* + * record_init - Initialize the recording module. + */ +void record_init() +{ + u8_t *b = buf; + + /* Write start marker */ + *b++ = 0x07; + *b++ = 0; + *b++ = 0; + *b++ = 0; + *b++ = 0; + ppp_dump_record(buf, b - buf); +} + +/* + * record_update - Updates the current time. + */ +void record_update(int ms) +{ + /* Recording timer resolution is 0.1 s */ + now += ms / 10; +} + +/* + * record_in - Dumps incoming data. + */ +void record_in(void *data, int len) +{ + write_record(2, data, len); +} + +/* + * record_out - Dumps outgoing data. + */ +void record_out(void *data, int len) +{ + write_record(1, data, len); +} + +#endif /* RECORD_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/record.h b/packages/net/lwip_tcpip/current/src/netif/ppp/record.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/record.h @@ -0,0 +1,50 @@ +/***************************************************************************** +* record.h - PPP recording header file. +* +* Copyright (c) 2009 by Simon Kallweit, intefo AG +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE 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 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. +* +****************************************************************************** +* REVISION HISTORY +* +* 09-02-03 Simon Kallweit +* First implementation. +*****************************************************************************/ + +#ifndef RECORD_H +#define RECORD_H + +/***************************************************************************** +************************** PUBLIC FUNCTIONS ********************************** +*****************************************************************************/ + +/* Initialize the recording module */ +void record_init(void); + +/* Updates the current time */ +void record_update(int ms); + +/* Dumps incoming data */ +void record_in(void *data, int len); + +/* Dumps outgoing data */ +void record_out(void *data, int len); + +#endif /* RECORD_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/timesys.c b/packages/net/lwip_tcpip/current/src/netif/ppp/timesys.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/timesys.c @@ -0,0 +1,155 @@ +/** + * @file + * System time and timeout system + * + */ + +/* + * Copyright (c) 2001-2004 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. + * + * Author: Simon Kallweit + */ + +#include "lwip/mem.h" + +#include "netif/ppp/ppp.h" +#include "netif/ppp/timesys.h" + +/* Milliseconds per jiffy */ +#define MS_PER_JIFFY 10 + +/* PPP timeout */ +struct ppp_timeout { + struct ppp_timeout *next; + ppp_timeout_handler_t handler; + void *arg; + u32_t timeout; +}; + +/* Current system time in ms */ +static u32_t current_time; + +/* Linked list of timeouts */ +static struct ppp_timeout *timeouts; + + + +/* + * Initialize the timesys. + */ +err_t +ppp_timesys_init(void) +{ + return ERR_OK; +} + +/* + * Reset the timesys. Removes all timeouts from the list. + */ +void +ppp_timesys_reset(void) +{ + struct ppp_timeout *t; + + for (t = timeouts; t; t = t->next) + t->handler = NULL; +} + +/* + * Update the timesys. + */ +void +ppp_timesys_update(u32_t delta) +{ + struct ppp_timeout *t; + + current_time += delta; + + /* Update timeouts */ + for (t = timeouts; t; t = t->next) + if (t->handler) { + if (t->timeout > delta) { + t->timeout -= delta; + } else { + t->handler(t->arg); + t->handler = NULL; + } + } +} + +/* + * Returns the current time. + */ +u32_t +ppp_jiffies(void) +{ + return current_time / MS_PER_JIFFY; +} + +/* + * Setup a timeout. + */ +void +ppp_timeout(ppp_timeout_handler_t handler, void *arg, int timeout) +{ + struct ppp_timeout *t; + + /* Reuse timeout slot if possible */ + for (t = timeouts; t; t = t->next) + if (!t->handler) { + goto setup_timeout; + } + + /* Allocate new timeout */ + t = mem_malloc(sizeof(struct ppp_timeout)); + LWIP_ASSERT("ppp_timeout: t != NULL", t != NULL); + + /* Append to list */ + t->next = timeouts; + timeouts = t; + +setup_timeout: + t->handler = handler; + t->arg = arg; + t->timeout = timeout * 1000; +} + +/* + * Clear a timeout. + */ +void +ppp_untimeout(ppp_timeout_handler_t handler, void *arg) +{ + struct ppp_timeout *t; + + /* Reuse timeout slot if possible */ + for (t = timeouts; t; t = t->next) + if ((t->handler == handler) && (t->arg == arg)) { + t->handler = NULL; + } +} diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/timesys.h b/packages/net/lwip_tcpip/current/src/netif/ppp/timesys.h new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/timesys.h @@ -0,0 +1,87 @@ +/** + * @file + * System time and timeout system + * + */ + +/* + * Copyright (c) 2001-2004 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. + * + * Author: Simon Kallweit + */ + +#ifndef TIMESYS_H +#define TIMESYS_H + +#include "lwip/sys.h" +#include "lwip/err.h" + +typedef void (*ppp_timeout_handler_t)(void *arg); + +#define TIMEOUT(f, a, t) ppp_timeout(f, a, t) +#define UNTIMEOUT(f, a) ppp_untimeout(f, a) + +/** + * Initialize the timesys. + * @return Returns ERR_OK if successful. + */ +err_t ppp_timesys_init(void); + +/** + * Reset the timesys. Removes all timeouts from the list. + */ +void ppp_timesys_reset(void); + +/** + * Update the timesys. + * @param delta Update interval in milliseconds. + */ +void ppp_timesys_update(u32_t delta); + +/** + * Returns the current time. + * @return Returns the time in jiffies. + */ +u32_t ppp_jiffies(void); + +/** + * Setup a timeout. + * @param handler Timeout handler callback + * @param arg Timeout handler argument + * @param timeout Timeout in seconds + */ +void ppp_timeout(ppp_timeout_handler_t handler, void *arg, int timeout); + +/** + * Clear a timeout. + * @param handler Timeout handler callback + * @param arg Timeout handler argument + */ +void ppp_untimeout(ppp_timeout_handler_t handler, void *arg); + +#endif /* TIMESYS_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/vj.c b/packages/net/lwip_tcpip/current/src/netif/ppp/vj.c --- a/packages/net/lwip_tcpip/current/src/netif/ppp/vj.c +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/vj.c @@ -17,8 +17,8 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: - * - Initial distribution. + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. * * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, * so that the entire packet being decompressed doesn't have @@ -28,13 +28,18 @@ * for a 16 bit processor. */ +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp.h" +#include "pppdebug.h" + +#include "vj.h" + #include -#include "ppp.h" -#include "vj.h" -#include "pppdebug.h" - -#if VJ_SUPPORT > 0 +#if VJ_SUPPORT #if LINK_STATS #define INCR(counter) ++comp->stats.counter @@ -43,33 +48,34 @@ #endif #if defined(NO_CHAR_BITFIELDS) -#define getip_hl(base) ((base).ip_hl_v&0xf) -#define getth_off(base) (((base).th_x2_off&0xf0)>>4) +#define getip_hl(base) ((base).ip_hl_v&0xf) +#define getth_off(base) (((base).th_x2_off&0xf0)>>4) #else -#define getip_hl(base) ((base).ip_hl) -#define getth_off(base) ((base).th_off) +#define getip_hl(base) ((base).ip_hl) +#define getth_off(base) ((base).th_off) #endif -void vj_compress_init(struct vjcompress *comp) +void +vj_compress_init(struct vjcompress *comp) { - register u_int i; - register struct cstate *tstate = comp->tstate; - + register u32_t i; + register struct cstate *tstate = comp->tstate; + #if MAX_SLOTS == 0 - memset((char *)comp, 0, sizeof(*comp)); + memset((char *)comp, 0, sizeof(*comp)); #endif - comp->maxSlotIndex = MAX_SLOTS - 1; - comp->compressSlot = 0; /* Disable slot ID compression by default. */ - for (i = MAX_SLOTS - 1; i > 0; --i) { - tstate[i].cs_id = i; - tstate[i].cs_next = &tstate[i - 1]; - } - tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; - tstate[0].cs_id = 0; - comp->last_cs = &tstate[0]; - comp->last_recv = 255; - comp->last_xmit = 255; - comp->flags = VJF_TOSS; + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; } @@ -78,55 +84,55 @@ void vj_compress_init(struct vjcompress * form). */ #define ENCODE(n) { \ - if ((u_short)(n) >= 256) { \ - *cp++ = 0; \ - cp[1] = (n); \ - cp[0] = (n) >> 8; \ - cp += 2; \ - } else { \ - *cp++ = (n); \ - } \ + if ((u16_t)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ } #define ENCODEZ(n) { \ - if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ - *cp++ = 0; \ - cp[1] = (n); \ - cp[0] = (n) >> 8; \ - cp += 2; \ - } else { \ - *cp++ = (n); \ - } \ + if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ } #define DECODEL(f) { \ - if (*cp == 0) {\ - u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ - (f) = htonl(tmp); \ - cp += 3; \ - } else { \ - u32_t tmp = ntohl(f) + (u32_t)*cp++; \ - (f) = htonl(tmp); \ - } \ + if (*cp == 0) {\ + u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = htonl(tmp); \ + cp += 3; \ + } else { \ + u32_t tmp = ntohl(f) + (u32_t)*cp++; \ + (f) = htonl(tmp); \ + } \ } #define DECODES(f) { \ - if (*cp == 0) {\ - u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \ - (f) = htons(tmp); \ - cp += 3; \ - } else { \ - u_short tmp = ntohs(f) + (u_short)*cp++; \ - (f) = htons(tmp); \ - } \ + if (*cp == 0) {\ + u16_t tmp = ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \ + (f) = htons(tmp); \ + cp += 3; \ + } else { \ + u16_t tmp = ntohs(f) + (u16_t)*cp++; \ + (f) = htons(tmp); \ + } \ } #define DECODEU(f) { \ - if (*cp == 0) {\ - (f) = htons(((u_short)cp[1] << 8) | cp[2]); \ - cp += 3; \ - } else { \ - (f) = htons((u_short)*cp++); \ - } \ + if (*cp == 0) {\ + (f) = htons(((u16_t)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u16_t)*cp++); \ + } \ } /* @@ -136,498 +142,519 @@ void vj_compress_init(struct vjcompress * Return the VJ type code indicating whether or not the packet was * compressed. */ -u_int vj_compress_tcp( - struct vjcompress *comp, - struct pbuf *pb -) +u32_t +vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) { - register struct ip *ip = (struct ip *)pb->payload; - register struct cstate *cs = comp->last_cs->cs_next; - register u_short hlen = getip_hl(*ip); - register struct tcphdr *oth; - register struct tcphdr *th; - register u_short deltaS, deltaA; - register u_long deltaL; - register u_int changes = 0; - u_char new_seq[16]; - register u_char *cp = new_seq; + register struct ip *ip = (struct ip *)pb->payload; + register struct cstate *cs = comp->last_cs->cs_next; + register u16_t hlen = getip_hl(*ip); + register struct tcphdr *oth; + register struct tcphdr *th; + register u16_t deltaS, deltaA; + register u32_t deltaL; + register u32_t changes = 0; + u8_t new_seq[16]; + register u8_t *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (ip->ip_p != IPPROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((ip->ip_off & htons(0x3fff)) || pb->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcphdr *)&((long *)ip)[hlen]; + if ((th->th_flags & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr + || ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr + || *(long *)th != ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr + && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr + && *(long *)th == ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + hlen += getth_off(*th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + return (TYPE_IP); + } + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcphdr *)&((long *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += getth_off(*th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + PPPDEBUG((LOG_INFO, "vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if (((u16_t *)ip)[0] != ((u16_t *)&cs->cs_ip)[0] + || ((u16_t *)ip)[3] != ((u16_t *)&cs->cs_ip)[3] + || ((u16_t *)ip)[4] != ((u16_t *)&cs->cs_ip)[4] + || getth_off(*th) != getth_off(*oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (getth_off(*th) > 5 && BCMP(th + 1, oth + 1, (getth_off(*th) - 5) << 2))) { + goto uncompressed; + } - /* - * Check that the packet is IP proto TCP. - */ - if (ip->ip_p != IPPROTO_TCP) - return (TYPE_IP); - - /* - * Bail if this is an IP fragment or if the TCP packet isn't - * `compressible' (i.e., ACK isn't set or some other control bit is - * set). - */ - if ((ip->ip_off & htons(0x3fff)) || pb->tot_len < 40) - return (TYPE_IP); - th = (struct tcphdr *)&((long *)ip)[hlen]; - if ((th->th_flags & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) - return (TYPE_IP); - - /* - * Packet is compressible -- we're going to send either a - * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need - * to locate (or create) the connection state. Special case the - * most recently used connection since it's most likely to be used - * again & we don't have to do any reordering if it's used. - */ - INCR(vjs_packets); - if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr - || ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr - || *(long *)th != ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { - /* - * Wasn't the first -- search for it. - * - * States are kept in a circularly linked list with - * last_cs pointing to the end of the list. The - * list is kept in lru order by moving a state to the - * head of the list whenever it is referenced. Since - * the list is short and, empirically, the connection - * we want is almost always near the front, we locate - * states via linear search. If we don't find a state - * for the datagram, the oldest state is (re-)used. - */ - register struct cstate *lcs; - register struct cstate *lastcs = comp->last_cs; - - do { - lcs = cs; cs = cs->cs_next; - INCR(vjs_searches); - if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr - && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr - && *(long *)th == ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) - goto found; - } while (cs != lastcs); - - /* - * Didn't find it -- re-use oldest cstate. Send an - * uncompressed packet that tells the other side what - * connection number we're using for this conversation. - * Note that since the state list is circular, the oldest - * state points to the newest and we only need to set - * last_cs to update the lru linkage. - */ - INCR(vjs_misses); - comp->last_cs = lcs; - hlen += getth_off(*th); - hlen <<= 2; - /* Check that the IP/TCP headers are contained in the first buffer. */ - if (hlen > pb->len) - return (TYPE_IP); - goto uncompressed; - - found: - /* - * Found it -- move to the front on the connection list. - */ - if (cs == lastcs) - comp->last_cs = lcs; - else { - lcs->cs_next = cs->cs_next; - cs->cs_next = lastcs->cs_next; - lastcs->cs_next = cs; - } - } - - oth = (struct tcphdr *)&((long *)&cs->cs_ip)[hlen]; - deltaS = hlen; - hlen += getth_off(*th); - hlen <<= 2; - /* Check that the IP/TCP headers are contained in the first buffer. */ - if (hlen > pb->len) { - PPPDEBUG((LOG_INFO, "vj_compress_tcp: header len %d spans buffers\n", - hlen)); - return (TYPE_IP); - } - - /* - * Make sure that only what we expect to change changed. The first - * line of the `if' checks the IP protocol version, header length & - * type of service. The 2nd line checks the "Don't fragment" bit. - * The 3rd line checks the time-to-live and protocol (the protocol - * check is unnecessary but costless). The 4th line checks the TCP - * header length. The 5th line checks IP options, if any. The 6th - * line checks TCP options, if any. If any of these things are - * different between the previous & current datagram, we send the - * current datagram `uncompressed'. - */ - if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] - || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] - || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] - || getth_off(*th) != getth_off(*oth) - || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) - || (getth_off(*th) > 5 && BCMP(th + 1, oth + 1, (getth_off(*th) - 5) << 2))) - goto uncompressed; - - /* - * Figure out which of the changing fields changed. The - * receiver expects changes in the order: urgent, window, - * ack, seq (the order minimizes the number of temporaries - * needed in this section of code). - */ - if (th->th_flags & TCP_URG) { - deltaS = ntohs(th->th_urp); - ENCODEZ(deltaS); - changes |= NEW_U; - } else if (th->th_urp != oth->th_urp) - /* argh! URG not set but urp changed -- a sensible - * implementation should never do this but RFC793 - * doesn't prohibit the change so we have to deal - * with it. */ - goto uncompressed; - - if ((deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) != 0) { - ENCODE(deltaS); - changes |= NEW_W; - } - - if ((deltaL = ntohl(th->th_ack) - ntohl(oth->th_ack)) != 0) { - if (deltaL > 0xffff) - goto uncompressed; - deltaA = (u_short)deltaL; - ENCODE(deltaA); - changes |= NEW_A; - } - - if ((deltaL = ntohl(th->th_seq) - ntohl(oth->th_seq)) != 0) { - if (deltaL > 0xffff) - goto uncompressed; - deltaS = (u_short)deltaL; - ENCODE(deltaS); - changes |= NEW_S; - } - - switch(changes) { - - case 0: - /* - * Nothing changed. If this packet contains data and the - * last one didn't, this is probably a data packet following - * an ack (normal on an interactive connection) and we send - * it compressed. Otherwise it's probably a retransmit, - * retransmitted ack or window probe. Send it uncompressed - * in case the other side missed the compressed version. - */ - if (ip->ip_len != cs->cs_ip.ip_len && - ntohs(cs->cs_ip.ip_len) == hlen) - break; - - /* (fall through) */ - - case SPECIAL_I: - case SPECIAL_D: - /* - * actual changes match one of our special case encodings -- - * send packet uncompressed. - */ - goto uncompressed; - - case NEW_S|NEW_A: - if (deltaS == deltaA && deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { - /* special case for echoed terminal traffic */ - changes = SPECIAL_I; - cp = new_seq; - } - break; - - case NEW_S: - if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { - /* special case for data xfer */ - changes = SPECIAL_D; - cp = new_seq; - } - break; - } - - deltaS = (u_short)(ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id)); - if (deltaS != 1) { - ENCODEZ(deltaS); - changes |= NEW_I; - } - if (th->th_flags & TCP_PSH) - changes |= TCP_PUSH_BIT; - /* - * Grab the cksum before we overwrite it below. Then update our - * state with this packet's header. - */ - deltaA = ntohs(th->th_sum); - BCOPY(ip, &cs->cs_ip, hlen); - - /* - * We want to use the original packet as our compressed packet. - * (cp - new_seq) is the number of bytes we need for compressed - * sequence numbers. In addition we need one byte for the change - * mask, one for the connection id and two for the tcp checksum. - * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how - * many bytes of the original packet to toss so subtract the two to - * get the new packet size. - */ - deltaS = (u_short)(cp - new_seq); - if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { - comp->last_xmit = cs->cs_id; - hlen -= deltaS + 4; - pbuf_header(pb, -hlen); - cp = (u_char *)pb->payload; - *cp++ = changes | NEW_C; - *cp++ = cs->cs_id; - } else { - hlen -= deltaS + 3; - pbuf_header(pb, -hlen); - cp = (u_char *)pb->payload; - *cp++ = changes; - } - *cp++ = deltaA >> 8; - *cp++ = deltaA; - BCOPY(new_seq, cp, deltaS); - INCR(vjs_compressed); - return (TYPE_COMPRESSED_TCP); + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (th->th_flags & TCP_URG) { + deltaS = ntohs(th->th_urp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->th_urp != oth->th_urp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u16_t)(ntohs(th->th_win) - ntohs(oth->th_win))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = ntohl(th->th_ack) - ntohl(oth->th_ack)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u16_t)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = ntohl(th->th_seq) - ntohl(oth->th_seq)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u16_t)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (ip->ip_len != cs->cs_ip.ip_len && + ntohs(cs->cs_ip.ip_len) == hlen) { + break; + } + + /* (fall through) */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; - /* - * Update connection state cs & send uncompressed packet (that is, - * a regular ip/tcp packet but with the 'conversation id' we hope - * to use on future compressed packets in the protocol field). - */ + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + deltaS = (u16_t)(ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id)); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (th->th_flags & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->th_sum); + BCOPY(ip, &cs->cs_ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u16_t)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if(pbuf_header(pb, -hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u8_t *)pb->payload; + *cp++ = changes | NEW_C; + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if(pbuf_header(pb, -hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u8_t *)pb->payload; + *cp++ = changes; + } + *cp++ = deltaA >> 8; + *cp++ = deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ uncompressed: - BCOPY(ip, &cs->cs_ip, hlen); - ip->ip_p = cs->cs_id; - comp->last_xmit = cs->cs_id; - return (TYPE_UNCOMPRESSED_TCP); + BCOPY(ip, &cs->cs_ip, hlen); + ip->ip_p = cs->cs_id; + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); } /* * Called when we may have missed a packet. */ -void vj_uncompress_err(struct vjcompress *comp) +void +vj_uncompress_err(struct vjcompress *comp) { - comp->flags |= VJF_TOSS; - INCR(vjs_errorin); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); } /* * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. * Return 0 on success, -1 on failure. */ -int vj_uncompress_uncomp( - struct pbuf *nb, - struct vjcompress *comp -) +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) { - register u_int hlen; - register struct cstate *cs; - register struct ip *ip; - - ip = (struct ip *)nb->payload; - hlen = getip_hl(*ip) << 2; - if (ip->ip_p >= MAX_SLOTS - || hlen + sizeof(struct tcphdr) > nb->len - || (hlen += getth_off(*((struct tcphdr *)&((char *)ip)[hlen])) << 2) - > nb->len - || hlen > MAX_HDR) { - PPPDEBUG((LOG_INFO, "vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", - ip->ip_p, hlen, nb->len)); - comp->flags |= VJF_TOSS; - INCR(vjs_errorin); - return -1; - } - cs = &comp->rstate[comp->last_recv = ip->ip_p]; - comp->flags &=~ VJF_TOSS; - ip->ip_p = IPPROTO_TCP; - BCOPY(ip, &cs->cs_ip, hlen); - cs->cs_hlen = hlen; - INCR(vjs_uncompressedin); - return 0; + register u32_t hlen; + register struct cstate *cs; + register struct ip *ip; + + ip = (struct ip *)nb->payload; + hlen = getip_hl(*ip) << 2; + if (ip->ip_p >= MAX_SLOTS + || hlen + sizeof(struct tcphdr) > nb->len + || (hlen += getth_off(*((struct tcphdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG((LOG_INFO, "vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + ip->ip_p, hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = ip->ip_p]; + comp->flags &=~ VJF_TOSS; + ip->ip_p = IPPROTO_TCP; + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_hlen = hlen; + INCR(vjs_uncompressedin); + return 0; } /* * Uncompress a packet of type TYPE_COMPRESSED_TCP. * The packet is composed of a buffer chain and the first buffer * must contain an accurate chain length. - * The first buffer must include the entire compressed TCP/IP header. + * The first buffer must include the entire compressed TCP/IP header. * This procedure replaces the compressed header with the uncompressed * header and returns the length of the VJ header. */ -int vj_uncompress_tcp( - struct pbuf **nb, - struct vjcompress *comp -) +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) { - u_char *cp; - struct tcphdr *th; - struct cstate *cs; - u_short *bp; - struct pbuf *n0 = *nb; - u32_t tmp; - u_int vjlen, hlen, changes; - - INCR(vjs_compressedin); - cp = (u_char *)n0->payload; - changes = *cp++; - if (changes & NEW_C) { - /* - * Make sure the state index is in range, then grab the state. - * If we have a good state index, clear the 'discard' flag. - */ - if (*cp >= MAX_SLOTS) { - PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: bad cid=%d\n", *cp)); - goto bad; - } - - comp->flags &=~ VJF_TOSS; - comp->last_recv = *cp++; - } else { - /* - * this packet has an implicit state index. If we've - * had a line error since the last time we got an - * explicit state index, we have to toss the packet. - */ - if (comp->flags & VJF_TOSS) { - PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: tossing\n")); - INCR(vjs_tossed); - return (-1); - } - } - cs = &comp->rstate[comp->last_recv]; - hlen = getip_hl(cs->cs_ip) << 2; - th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; - th->th_sum = htons((*cp << 8) | cp[1]); - cp += 2; - if (changes & TCP_PUSH_BIT) - th->th_flags |= TCP_PSH; - else - th->th_flags &=~ TCP_PSH; - - switch (changes & SPECIALS_MASK) { - case SPECIAL_I: - { - register u32_t i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; - /* some compilers can't nest inline assembler.. */ - tmp = ntohl(th->th_ack) + i; - th->th_ack = htonl(tmp); - tmp = ntohl(th->th_seq) + i; - th->th_seq = htonl(tmp); - } - break; - - case SPECIAL_D: - /* some compilers can't nest inline assembler.. */ - tmp = ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; - th->th_seq = htonl(tmp); - break; - - default: - if (changes & NEW_U) { - th->th_flags |= TCP_URG; - DECODEU(th->th_urp); - } else - th->th_flags &=~ TCP_URG; - if (changes & NEW_W) - DECODES(th->th_win); - if (changes & NEW_A) - DECODEL(th->th_ack); - if (changes & NEW_S) - DECODEL(th->th_seq); - break; - } - if (changes & NEW_I) { - DECODES(cs->cs_ip.ip_id); - } else { - cs->cs_ip.ip_id = ntohs(cs->cs_ip.ip_id) + 1; - cs->cs_ip.ip_id = htons(cs->cs_ip.ip_id); - } - - /* - * At this point, cp points to the first byte of data in the - * packet. Fill in the IP total length and update the IP - * header checksum. - */ - vjlen = (u_short)(cp - (u_char*)n0->payload); - if (n0->len < vjlen) { - /* - * We must have dropped some characters (crc should detect - * this but the old slip framing won't) - */ - PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: head buffer %d too short %d\n", - n0->len, vjlen)); - goto bad; - } - + u8_t *cp; + struct tcphdr *th; + struct cstate *cs; + u16_t *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u32_t vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u8_t *)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = getip_hl(cs->cs_ip) << 2; + th = (struct tcphdr *)&((u8_t *)&cs->cs_ip)[hlen]; + th->th_sum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + th->th_flags |= TCP_PSH; + } else { + th->th_flags &=~ TCP_PSH; + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u32_t i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->th_ack) + i; + th->th_ack = htonl(tmp); + tmp = ntohl(th->th_seq) + i; + th->th_seq = htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + th->th_seq = htonl(tmp); + break; + + default: + if (changes & NEW_U) { + th->th_flags |= TCP_URG; + DECODEU(th->th_urp); + } else { + th->th_flags &=~ TCP_URG; + } + if (changes & NEW_W) { + DECODES(th->th_win); + } + if (changes & NEW_A) { + DECODEL(th->th_ack); + } + if (changes & NEW_S) { + DECODEL(th->th_seq); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip.ip_id); + } else { + cs->cs_ip.ip_id = ntohs(cs->cs_ip.ip_id) + 1; + cs->cs_ip.ip_id = htons(cs->cs_ip.ip_id); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u16_t)(cp - (u8_t*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + #if BYTE_ORDER == LITTLE_ENDIAN - tmp = n0->tot_len - vjlen + cs->cs_hlen; - cs->cs_ip.ip_len = htons(tmp); + tmp = n0->tot_len - vjlen + cs->cs_hlen; + cs->cs_ip.ip_len = htons(tmp); #else - cs->cs_ip.ip_len = htons(n0->tot_len - vjlen + cs->cs_hlen); + cs->cs_ip.ip_len = htons(n0->tot_len - vjlen + cs->cs_hlen); #endif - - /* recompute the ip header checksum */ - bp = (u_short *) &cs->cs_ip; - cs->cs_ip.ip_sum = 0; - for (tmp = 0; hlen > 0; hlen -= 2) - tmp += *bp++; - tmp = (tmp & 0xffff) + (tmp >> 16); - tmp = (tmp & 0xffff) + (tmp >> 16); - cs->cs_ip.ip_sum = (u_short)(~tmp); - - /* Remove the compressed header and prepend the uncompressed header. */ - pbuf_header(n0, -vjlen); + + /* recompute the ip header checksum */ + bp = (u16_t *) &cs->cs_ip; + cs->cs_ip.ip_sum = 0; + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += *bp++; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + cs->cs_ip.ip_sum = (u16_t)(~tmp); - if(MEM_ALIGN(n0->payload) != n0->payload) { - struct pbuf *np, *q; - u8_t *bufptr; + /* Remove the compressed header and prepend the uncompressed header. */ + if(pbuf_header(n0, -((s16_t)(vjlen)))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } - np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); - if(!np) { - PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: realign failed\n")); - *nb = NULL; - goto bad; - } + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; - pbuf_header(np, -cs->cs_hlen); + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: realign failed\n")); + goto bad; + } - bufptr = n0->payload; - for(q = np; q != NULL; q = q->next) { - memcpy(q->payload, bufptr, q->len); - bufptr += q->len; - } + if(pbuf_header(np, -cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } - if(n0->next) { - pbuf_chain(np, n0->next); - pbuf_dechain(n0); - } - pbuf_free(n0); - n0 = np; - } + bufptr = n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } - if(pbuf_header(n0, cs->cs_hlen)) { - struct pbuf *np; + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } - LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); - np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); - if(!np) { - PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: prepend failed\n")); - *nb = NULL; - goto bad; - } - pbuf_cat(np, n0); - n0 = np; - } - LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); - memcpy(n0->payload, &cs->cs_ip, cs->cs_hlen); + if(pbuf_header(n0, cs->cs_hlen)) { + struct pbuf *np; - *nb = n0; + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); - return vjlen; - + *nb = n0; + + return vjlen; + bad: - comp->flags |= VJF_TOSS; - INCR(vjs_errorin); - return (-1); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); } -#endif +#endif /* VJ_SUPPORT */ - +#endif /* PPP_SUPPORT */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/vj.h b/packages/net/lwip_tcpip/current/src/netif/ppp/vj.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/vj.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/vj.h @@ -1,7 +1,7 @@ /* * Definitions for tcp compression routines. * - * $Id: vj.h,v 1.2 2006/03/29 10:33:28 asl Exp $ + * $Id: vj.h,v 1.3 2010/01/26 11:27:49 jld Exp $ * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. @@ -18,8 +18,8 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: - * - Initial distribution. + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. */ #ifndef VJ_H @@ -27,8 +27,8 @@ #include "vjbsdhdr.h" -#define MAX_SLOTS 16 /* must be > 2 and < 256 */ -#define MAX_HDR 128 +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 /* * Compressed packet format: @@ -42,7 +42,7 @@ * sequence number changes, one change per bit set in the header * (there may be no changes and there are two special cases where * the receiver implicitly knows what changed -- see below). - * + * * There are 5 numbers which can change (they are always inserted * in the following order): TCP urgent pointer, window, * acknowlegement, sequence number and IP ID. (The urgent pointer @@ -74,22 +74,22 @@ */ /* packet types */ -#define TYPE_IP 0x40 +#define TYPE_IP 0x40 #define TYPE_UNCOMPRESSED_TCP 0x70 -#define TYPE_COMPRESSED_TCP 0x80 -#define TYPE_ERROR 0x00 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 /* Bits in first octet of compressed packet */ -#define NEW_C 0x40 /* flag bits for what changed in a packet */ -#define NEW_I 0x20 -#define NEW_S 0x08 -#define NEW_A 0x04 -#define NEW_W 0x02 -#define NEW_U 0x01 +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 /* reserved, special-case values of above */ -#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ -#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) #define TCP_PUSH_BIT 0x10 @@ -102,54 +102,54 @@ * the transmit & receive ends of the line use to locate saved header. */ struct cstate { - struct cstate *cs_next; /* next most recently used state (xmit only) */ - u_short cs_hlen; /* size of hdr (receive only) */ - u_char cs_id; /* connection # associated with this state */ - u_char cs_filler; - union { - char csu_hdr[MAX_HDR]; - struct ip csu_ip; /* ip/tcp hdr from most recent packet */ - } vjcs_u; + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u16_t cs_hlen; /* size of hdr (receive only) */ + u8_t cs_id; /* connection # associated with this state */ + u8_t cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; }; #define cs_ip vjcs_u.csu_ip #define cs_hdr vjcs_u.csu_hdr struct vjstat { - unsigned long vjs_packets; /* outbound packets */ - unsigned long vjs_compressed; /* outbound compressed packets */ - unsigned long vjs_searches; /* searches for connection state */ - unsigned long vjs_misses; /* times couldn't find conn. state */ - unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ - unsigned long vjs_compressedin; /* inbound compressed packets */ - unsigned long vjs_errorin; /* inbound unknown type packets */ - unsigned long vjs_tossed; /* inbound packets tossed because of error */ + unsigned long vjs_packets; /* outbound packets */ + unsigned long vjs_compressed; /* outbound compressed packets */ + unsigned long vjs_searches; /* searches for connection state */ + unsigned long vjs_misses; /* times couldn't find conn. state */ + unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned long vjs_compressedin; /* inbound compressed packets */ + unsigned long vjs_errorin; /* inbound unknown type packets */ + unsigned long vjs_tossed; /* inbound packets tossed because of error */ }; /* * all the state data for one serial line (we need one of these per line). */ struct vjcompress { - struct cstate *last_cs; /* most recently used tstate */ - u_char last_recv; /* last rcvd conn. id */ - u_char last_xmit; /* last sent conn. id */ - u_short flags; - u_char maxSlotIndex; - u_char compressSlot; /* Flag indicating OK to compress slot ID. */ + struct cstate *last_cs; /* most recently used tstate */ + u8_t last_recv; /* last rcvd conn. id */ + u8_t last_xmit; /* last sent conn. id */ + u16_t flags; + u8_t maxSlotIndex; + u8_t compressSlot; /* Flag indicating OK to compress slot ID. */ #if LINK_STATS - struct vjstat stats; + struct vjstat stats; #endif - struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ - struct cstate rstate[MAX_SLOTS]; /* receive connection states */ + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ }; /* flag values */ -#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ -extern void vj_compress_init (struct vjcompress *comp); -extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); -extern void vj_uncompress_err (struct vjcompress *comp); -extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); -extern int vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp); +extern void vj_compress_init (struct vjcompress *comp); +extern u32_t vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); #endif /* VJ_H */ diff --git a/packages/net/lwip_tcpip/current/src/netif/ppp/vjbsdhdr.h b/packages/net/lwip_tcpip/current/src/netif/ppp/vjbsdhdr.h --- a/packages/net/lwip_tcpip/current/src/netif/ppp/vjbsdhdr.h +++ b/packages/net/lwip_tcpip/current/src/netif/ppp/vjbsdhdr.h @@ -3,11 +3,10 @@ #include "lwip/tcp.h" - /* * Structure of an internet header, naked of options. * - * We declare ip_len and ip_off to be short, rather than u_short + * We declare ip_len and ip_off to be short, rather than u16_t * pragmatically since otherwise unsigned comparisons can result * against negative integers quite easily, and fail in subtle ways. */ @@ -15,29 +14,29 @@ PACK_STRUCT_BEGIN struct ip { #if defined(NO_CHAR_BITFIELDS) - u_char ip_hl_v; /* bug in GCC for mips means the bitfield stuff will sometimes break - so we use a char for both and get round it with macro's instead... */ + u8_t ip_hl_v; /* bug in GCC for mips means the bitfield stuff will sometimes break - so we use a char for both and get round it with macro's instead... */ #else #if BYTE_ORDER == LITTLE_ENDIAN - unsigned ip_hl:4, /* header length */ - ip_v:4; /* version */ -#elif BYTE_ORDER == BIG_ENDIAN - unsigned ip_v:4, /* version */ - ip_hl:4; /* header length */ + unsigned ip_hl:4, /* header length */ + ip_v :4; /* version */ +#elif BYTE_ORDER == BIG_ENDIAN + unsigned ip_v :4, /* version */ + ip_hl:4; /* header length */ #else - COMPLAIN - NO BYTE ORDER SELECTED! + COMPLAIN - NO BYTE ORDER SELECTED! #endif #endif - u_char ip_tos; /* type of service */ - u_short ip_len; /* total length */ - u_short ip_id; /* identification */ - u_short ip_off; /* fragment offset field */ -#define IP_DF 0x4000 /* dont fragment flag */ -#define IP_MF 0x2000 /* more fragments flag */ -#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ - u_char ip_ttl; /* time to live */ - u_char ip_p; /* protocol */ - u_short ip_sum; /* checksum */ - struct in_addr ip_src,ip_dst; /* source and dest address */ + u8_t ip_tos; /* type of service */ + u16_t ip_len; /* total length */ + u16_t ip_id; /* identification */ + u16_t ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u8_t ip_ttl; /* time to live */ + u8_t ip_p; /* protocol */ + u16_t ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ }; PACK_STRUCT_END @@ -48,28 +47,28 @@ typedef u32_t tcp_seq; * Per RFC 793, September, 1981. */ PACK_STRUCT_BEGIN -struct tcphdr +struct tcphdr { - u_short th_sport; /* source port */ - u_short th_dport; /* destination port */ - tcp_seq th_seq; /* sequence number */ - tcp_seq th_ack; /* acknowledgement number */ + u16_t th_sport; /* source port */ + u16_t th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ #if defined(NO_CHAR_BITFIELDS) - u_char th_x2_off; + u8_t th_x2_off; #else #if BYTE_ORDER == LITTLE_ENDIAN - unsigned th_x2:4, /* (unused) */ - th_off:4; /* data offset */ + unsigned th_x2 :4, /* (unused) */ + th_off:4; /* data offset */ #endif -#if BYTE_ORDER == BIG_ENDIAN - unsigned th_off:4, /* data offset */ - th_x2:4; /* (unused) */ +#if BYTE_ORDER == BIG_ENDIAN + unsigned th_off:4, /* data offset */ + th_x2 :4; /* (unused) */ #endif #endif - u_char th_flags; - u_short th_win; /* window */ - u_short th_sum; /* checksum */ - u_short th_urp; /* urgent pointer */ + u8_t th_flags; + u16_t th_win; /* window */ + u16_t th_sum; /* checksum */ + u16_t th_urp; /* urgent pointer */ }; PACK_STRUCT_END diff --git a/packages/net/lwip_tcpip/current/src/netif/slipif.c b/packages/net/lwip_tcpip/current/src/netif/slipif.c --- a/packages/net/lwip_tcpip/current/src/netif/slipif.c +++ b/packages/net/lwip_tcpip/current/src/netif/slipif.c @@ -1,3 +1,9 @@ +/** + * @file + * SLIP Interface + * + */ + /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. @@ -33,97 +39,163 @@ /* * This is an arch independent SLIP netif. The specific serial hooks must be - * provided by another file. They are sio_open, sio_recv and sio_send + * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send */ #include "netif/slipif.h" #include "lwip/opt.h" + +#if LWIP_HAVE_SLIPIF + #include "lwip/def.h" #include "lwip/pbuf.h" #include "lwip/sys.h" #include "lwip/stats.h" +#include "lwip/snmp.h" #include "lwip/sio.h" -#define SLIP_END 0300 -#define SLIP_ESC 0333 -#define SLIP_ESC_END 0334 -#define SLIP_ESC_ESC 0335 +#define SLIP_BLOCK 1 +#define SLIP_DONTBLOCK 0 + +#define SLIP_END 0300 /* 0xC0 */ +#define SLIP_ESC 0333 /* 0xDB */ +#define SLIP_ESC_END 0334 /* 0xDC */ +#define SLIP_ESC_ESC 0335 /* 0xDD */ + +#define SLIP_MAX_SIZE 1500 -#define MAX_SIZE 1500 +enum slipif_recv_state { + SLIP_RECV_NORMAL, + SLIP_RECV_ESCAPE, +}; + +struct slipif_priv { + sio_fd_t sd; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + enum slipif_recv_state state; + u16_t i, recved; +}; /** * Send a pbuf doing the necessary SLIP encapsulation * - * Uses the serial layer's sio_send() + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values */ err_t slipif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) { + struct slipif_priv *priv; struct pbuf *q; u16_t i; u8_t c; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_UNUSED_ARG(ipaddr); + + priv = netif->state; + /* Send pbuf out on the serial I/O device. */ - sio_send(SLIP_END, netif->state); + sio_send(SLIP_END, priv->sd); for (q = p; q != NULL; q = q->next) { for (i = 0; i < q->len; i++) { c = ((u8_t *)q->payload)[i]; switch (c) { case SLIP_END: - sio_send(SLIP_ESC, netif->state); - sio_send(SLIP_ESC_END, netif->state); + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_END, priv->sd); break; case SLIP_ESC: - sio_send(SLIP_ESC, netif->state); - sio_send(SLIP_ESC_ESC, netif->state); + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_ESC, priv->sd); break; default: - sio_send(c, netif->state); + sio_send(c, priv->sd); break; } } } - sio_send(SLIP_END, netif->state); - return 0; + sio_send(SLIP_END, priv->sd); + return ERR_OK; +} + +/** + * Static function for easy use of blockig or non-blocking + * sio_read + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @param block if 1, call sio_read; if 0, call sio_tryread + * @return return value of sio_read of sio_tryread + */ +static u32_t +slip_sio_read(sio_fd_t fd, u8_t* data, u32_t len, u8_t block) +{ + if (block) { + return sio_read(fd, data, len); + } else { + return sio_tryread(fd, data, len); + } } /** * Handle the incoming SLIP stream character by character * - * Poll the serial layer by calling sio_recv() - * - * @return The IP packet when SLIP_END is received + * Poll the serial layer by calling sio_read() or sio_tryread(). + * + * @param netif the lwip network interface structure for this slipif + * @param block if 1, block until data is received; if 0, return when all data + * from the buffer is received (multiple calls to this function will + * return a complete packet, NULL is returned before - used for polling) + * @return The IP packet when SLIP_END is received */ static struct pbuf * -slipif_input(struct netif *netif) +slipif_input(struct netif *netif, u8_t block) { + struct slipif_priv *priv; u8_t c; - struct pbuf *p, *q; - u16_t recved; - u16_t i; + struct pbuf *t; - q = p = NULL; - recved = i = 0; - c = 0; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; - while (1) { - c = sio_recv(netif->state); - switch (c) { - case SLIP_END: - if (recved > 0) { - /* Received whole packet. */ - pbuf_realloc(q, recved); - - LINK_STATS_INC(link.recv); - - LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n")); - return q; + while (slip_sio_read(priv->sd, &c, 1, block) > 0) { + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n")); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; + } + continue; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + continue; } break; - - case SLIP_ESC: - c = sio_recv(netif->state); + case SLIP_RECV_ESCAPE: switch (c) { case SLIP_ESC_END: c = SLIP_END; @@ -132,82 +204,164 @@ slipif_input(struct netif *netif) c = SLIP_ESC; break; } + priv->state = SLIP_RECV_NORMAL; /* FALLTHROUGH */ + } - default: - if (p == NULL) { - LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); - p = pbuf_alloc(PBUF_LINK, PBUF_POOL_BUFSIZE, PBUF_POOL); + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); + + if (priv->p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + break; + } - if (p == NULL) { - LINK_STATS_INC(link.drop); - LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); - } + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; + } + } - if (q != NULL) { - pbuf_cat(q, p); + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; } else { - q = p; + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; } } - if (p != NULL && recved < MAX_SIZE) { - ((u8_t *)p->payload)[i] = c; - recved++; - i++; - if (i >= p->len) { - i = 0; - if (p->next != NULL && p->next->len > 0) - p = p->next; - else - p = NULL; - } - } - break; } + } - } return NULL; } +#if !NO_SYS /** * The SLIP input thread. * * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif */ static void -slipif_loop(void *nf) +slipif_loop_thread(void *nf) { struct pbuf *p; struct netif *netif = (struct netif *)nf; while (1) { - p = slipif_input(netif); - netif->input(p, netif); + p = slipif_input(netif, SLIP_BLOCK); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } } } +#endif /* !NO_SYS */ /** * SLIP netif initialization * * Call the arch specific sio_open and remember * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_MEM if no memory could be allocated, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default) */ err_t slipif_init(struct netif *netif) { + struct slipif_priv *priv; LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + /* Allocate private data */ + priv = mem_malloc(sizeof(struct slipif_priv)); + if (!priv) { + return ERR_MEM; + } + netif->name[0] = 's'; netif->name[1] = 'l'; netif->output = slipif_output; - netif->mtu = 1500; + netif->mtu = SLIP_MAX_SIZE; netif->flags |= NETIF_FLAG_POINTTOPOINT; - netif->state = sio_open(netif->num); - if (!netif->state) + /* Try to open the serial port (netif->num contains the port number). */ + priv->sd = sio_open(netif->num); + if (!priv->sd) { + /* Opening the serial port failed. */ + mem_free(priv); return ERR_IF; + } - sys_thread_new(slipif_loop, netif, SLIPIF_THREAD_PRIO); + /* Initialize private data */ + priv->p = NULL; + priv->q = NULL; + priv->state = SLIP_RECV_NORMAL; + priv->i = 0; + priv->recved = 0; + + netif->state = priv; + + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made without knowing more about the + * serial line! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, 0); + + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, + SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); return ERR_OK; } + +/** + * Polls the serial device and feeds the IP layer with incoming packets. + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_poll(struct netif *netif) +{ + struct pbuf *p; + struct slipif_priv *priv; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; + + while ((p = slipif_input(netif, SLIP_DONTBLOCK)) != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#endif /* LWIP_HAVE_SLIPIF */ diff --git a/packages/net/lwip_tcpip/current/tests/httpd_sequential.c b/packages/net/lwip_tcpip/current/tests/httpd_sequential.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/tests/httpd_sequential.c @@ -0,0 +1,249 @@ +//========================================================================== +// +// httpd_sequential.c +// +// A simple HTTP server using the netconn API in 'Sequential' mode. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2009-05-14 +// Purpose: +// Description: A simple HTTP server using the netconn API in 'Sequential' +// mode. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include + +#include +#include + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL +#if LWIP_TCP +#if LWIP_NETCONN + +struct http_state { + char buf[256]; + char *file; + u32_t left; + u8_t retries; +}; + +static int request_counter = 1; + +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL + +static char main_stack[STACK_SIZE]; +static cyg_thread main_thread; +static cyg_handle_t main_handle; + + +// Sends a text. +static void +send_text(struct netconn *conn, const char *text) +{ + const char *src = text; + u16_t len = 0; + + while (*src++ != '\0') + len++; + + netconn_write(conn, text, len, NETCONN_COPY); +} + +// Sends a decimal value. +static void +send_dec(struct netconn *conn, int value) +{ + char tmp1[16], tmp2[16]; + int i = 0, j = 0; + + do { + tmp1[i++] = '0' + value % 10; + value /= 10; + } while (value > 0); + + while (i-- > 0) + tmp2[j++] = tmp1[i]; + + netconn_write(conn, tmp2, j, NETCONN_COPY); +} + +// Sends the HTTP page. +static void +send_page(struct netconn *conn) +{ + send_text(conn, "HTTP/1.0 200 OK\r\n"); + send_text(conn, "Content-Type: text/html\r\n"); + send_text(conn, "\r\n"); + send_text(conn, "\r\n"); + send_text(conn, "

eCos - HTTP test server running on lwIP

\r\n"); + send_text(conn, "
Version:"); + send_dec(conn, LWIP_VERSION_MAJOR); + send_text(conn, "."); + send_dec(conn, LWIP_VERSION_MINOR); + send_text(conn, "."); + send_dec(conn, LWIP_VERSION_REVISION); + if (LWIP_VERSION_IS_RELEASE) + send_text(conn, " (release)"); + if (LWIP_VERSION_IS_DEVELOPMENT) + send_text(conn, " (development)"); + if (LWIP_VERSION_IS_RC) + send_text(conn, " (rc)"); + send_text(conn, "
Requests:"); + send_dec(conn, request_counter++); + send_text(conn, "
\r\n"); + send_text(conn, ""); +} + +static void +http_server_serve(struct netconn *conn) +{ + struct netbuf *inbuf; + char *buf; + u16_t len; + + // Read the data from the port, blocking if nothing yet there. + // We assume the request (the part we care about) is in one netbuf. + inbuf = netconn_recv(conn); + if (!inbuf) + return; + + if (netconn_err(conn) == ERR_OK) { + + // Get data from the netbuf + netbuf_data(inbuf, (void **) &buf, &len); + + // Is this an HTTP GET command? Only check the first 5 chars, since + // there are other formats for GET, and we're keeping it very simple. + if (len >= 5 && buf[0] == 'G' && buf[1] == 'E' && buf[2] == 'T' && + buf[3] == ' ' && buf[4] == '/' ) { + + // Send the response + send_page(conn); + } + } + + // Delete the buffer (netconn_recv gives us ownership, so we have to make + // sure to deallocate the buffer). + netbuf_delete(inbuf); +} + + +// Main thread. +void +main_thread_entry(cyg_addrword_t p) +{ + struct netconn *server, *client; + + CYG_TEST_INFO("Initializing lwIP"); + cyg_lwip_sequential_init(); + + // Create a new TCP connection handle + CYG_TEST_INFO("Initializing server"); + server = netconn_new(NETCONN_TCP); + if (server == NULL) + CYG_TEST_FAIL_FINISH("Cannot allocate server netconn"); + + // Bind to port 80 (HTTP) with default IP address + netconn_bind(server, NULL, 80); + + // Put the connection into LISTEN state + netconn_listen(server); + + // Accept client connections + CYG_TEST_INFO("Running"); + while(1) { + client = netconn_accept(server); + http_server_serve(client); + netconn_close(client); + netconn_delete(client); + } +} + +void +httpd_main(void) +{ + CYG_TEST_INIT(); + CYG_TEST_INFO("Testing httpd"); + + cyg_thread_create( + 10, // Priority + main_thread_entry, // Entry + 0, // Entry parameter + "main", // Name + main_stack, // Stack + STACK_SIZE, // Size + &main_handle, // Handle + &main_thread // Thread data structure + ); + cyg_thread_resume(main_handle); + cyg_scheduler_start(); + + CYG_TEST_FAIL_FINISH("Not reached"); +} + +externC void +cyg_start(void) +{ + httpd_main(); +} + +#else // LWIP_NETCONN +#define N_A_MSG "Netconn support disabled" +#endif // LWIP_NETCONN + +#else // LWIP_TCP +#define N_A_MSG "TCP support disabled" +#endif // LWIP_TCP + +#else // CYGFUN_LWIP_MODE_SEQUENTIAL +#define N_A_MSG "Not configured in 'Sequential' mode" +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL + +#ifdef N_A_MSG +externC void +cyg_start(void) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG diff --git a/packages/net/lwip_tcpip/current/tests/httpd_simple.c b/packages/net/lwip_tcpip/current/tests/httpd_simple.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/tests/httpd_simple.c @@ -0,0 +1,406 @@ +//========================================================================== +// +// httpd_simple.c +// +// A simple HTTP server using raw tcp pcbs in 'Simple' mode. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2008-12-01 +// Purpose: +// Description: A simple HTTP server using raw tcp pcbs in 'Simple' mode. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include + +#include +#include + +#ifdef CYGFUN_LWIP_MODE_SIMPLE +#if LWIP_TCP + +struct http_state { + char buf[256]; + char *file; + u32_t left; + u8_t retries; +}; + +static int request_counter = 1; + +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL + +static char main_stack[STACK_SIZE]; +static cyg_thread main_thread; +static cyg_handle_t main_handle; + + +// Appends text to the buffer. +static void +buf_append_text(char *buf, const char *text) +{ + char *dst = buf; + const char *src = text; + + while (*dst != '\0') + dst++; + + while (*src != '\0') + *dst++ = *src++; + + *dst = '\0'; +} + +// Appends a decimal value to the buffer. +static void +buf_append_dec(char *buf, int value) +{ + char *dst = buf; + char tmp[16]; + int i = 0; + + while (*dst != '\0') + dst++; + + do { + tmp[i++] = '0' + value % 10; + value /= 10; + } while (value > 0); + + while (i-- > 0) + *dst++ = tmp[i]; + + *dst = '\0'; +} + +// Returns the length of the buffer. +static size_t +buf_length(char *buf) +{ + size_t len = 0; + + while (*buf++ != '\0') + len++; + + return len; +} + +// Prepares the HTTP page. +static void +prepare_page(struct http_state *hs) +{ + hs->buf[0] = '\0'; + buf_append_text(hs->buf, "HTTP/1.0 200 OK\r\n"); + buf_append_text(hs->buf, "Content-Type: text/html\r\n"); + buf_append_text(hs->buf, "\r\n"); + buf_append_text(hs->buf, "\r\n"); + buf_append_text(hs->buf, "

eCos - HTTP test server running on lwIP

\r\n"); + buf_append_text(hs->buf, "
Version:"); + buf_append_dec(hs->buf, LWIP_VERSION_MAJOR); + buf_append_text(hs->buf, "."); + buf_append_dec(hs->buf, LWIP_VERSION_MINOR); + buf_append_text(hs->buf, "."); + buf_append_dec(hs->buf, LWIP_VERSION_REVISION); + if (LWIP_VERSION_IS_RELEASE) + buf_append_text(hs->buf, " (release)"); + if (LWIP_VERSION_IS_DEVELOPMENT) + buf_append_text(hs->buf, " (development)"); + if (LWIP_VERSION_IS_RC) + buf_append_text(hs->buf, " (rc)"); + buf_append_text(hs->buf, "
Requests:"); + buf_append_dec(hs->buf, request_counter++); + buf_append_text(hs->buf, "
\r\n"); + buf_append_text(hs->buf, ""); + + hs->file = hs->buf; + hs->left = buf_length(hs->buf); +} + +// Called when an error occured. Frees the allocated resources. +static void +http_err(void *arg, err_t err) +{ + struct http_state *hs; + + CYG_TEST_INFO("Connection error"); + + hs = arg; + mem_free(hs); +} + +// Closes the TCP connection and frees the allocated resources. +static void +close_conn(struct tcp_pcb *pcb, struct http_state *hs) +{ + CYG_TEST_INFO("Closing connection"); + + tcp_arg(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_recv(pcb, NULL); + mem_free(hs); + tcp_close(pcb); +} + +// Sends the next chunk of data. +static void +send_data(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err; + u16_t len; + + CYG_TEST_INFO("Sending data"); + + // We cannot send more data than space available in the send buffer + len = tcp_sndbuf(pcb) < hs->left ? tcp_sndbuf(pcb) : hs->left; + + do { + err = tcp_write(pcb, hs->file, len, 0); + if (err == ERR_MEM) { + len /= 2; + } + } while (err == ERR_MEM && len > 1); + + if (err == ERR_OK) { + hs->file += len; + hs->left -= len; + } +} + +// Called in a regular interval. Implements retries. +static err_t +http_poll(void *arg, struct tcp_pcb *pcb) +{ + struct http_state *hs; + + CYG_TEST_INFO("Polling"); + + hs = arg; + + if (hs == NULL) { + tcp_abort(pcb); + return ERR_ABRT; + } else { + ++hs->retries; + if (hs->retries == 4) { + tcp_abort(pcb); + return ERR_ABRT; + } + send_data(pcb, hs); + } + + return ERR_OK; +} + +// Called when data has been sent. +static err_t +http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct http_state *hs; + + CYG_TEST_INFO("Sent data"); + + hs = arg; + + hs->retries = 0; + + if (hs->left > 0) { + send_data(pcb, hs); + } else { + close_conn(pcb, hs); + } + + return ERR_OK; +} + +// Called when data has been received. Checks for HTTP requests. +static err_t +http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + int i; + char *data; + struct http_state *hs; + + CYG_TEST_INFO("Received data"); + + hs = arg; + + if (err == ERR_OK && p != NULL) { + // Inform TCP that we have taken the data + tcp_recved(pcb, p->tot_len); + + // Check for HTTP requests + if (hs->file == NULL) { + data = p->payload; + + if (*data == 'G') { + for (i = 0; i < 40; i++) { + if (((char *) data + 4)[i] == ' ' || + ((char *) data + 4)[i] == '\r' || + ((char *) data + 4)[i] == '\n') { + ((char *)data + 4)[i] = 0; + } + } + + prepare_page(hs); + + pbuf_free(p); + send_data(pcb, hs); + + // Tell TCP that we wish be to informed of data that has been + // successfully sent by a call to the http_sent() function. + tcp_sent(pcb, http_sent); + } else { + pbuf_free(p); + close_conn(pcb, hs); + } + } else { + pbuf_free(p); + } + } + + if (err == ERR_OK && p == NULL) { + close_conn(pcb, hs); + } + + return ERR_OK; +} + +// Called when a new connection was accepted. +static err_t +http_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct http_state *hs; + + CYG_TEST_INFO("Incoming connection"); + + tcp_setprio(pcb, TCP_PRIO_MIN); + + // Allocate memory for the structure that holds the state of the connection + hs = mem_malloc(sizeof(struct http_state)); + if (hs == NULL) + return ERR_MEM; + + // Initialize the structure + hs->file = NULL; + hs->left = 0; + hs->retries = 0; + + // Tell TCP that this is the structure we wish to be passed for our + // callbacks + tcp_arg(pcb, hs); + + // Register callbacks + tcp_recv(pcb, http_recv); + tcp_err(pcb, http_err); + tcp_poll(pcb, http_poll, 4); + + CYG_TEST_INFO("Connection accepted"); + + return ERR_OK; +} + +// Main thread. +void +main_thread_entry(cyg_addrword_t p) +{ + struct tcp_pcb *pcb; + + CYG_TEST_INFO("Initializing lwIP"); + cyg_lwip_simple_init(); + + CYG_TEST_INFO("Initializing tcb"); + pcb = tcp_new(); + tcp_bind(pcb, IP_ADDR_ANY, 80); + pcb = tcp_listen(pcb); + tcp_accept(pcb, http_accept); + + CYG_TEST_INFO("Running"); + while (1) { + cyg_lwip_simple_poll(); + cyg_thread_delay(1); + } +} + +void +httpd_main(void) +{ + CYG_TEST_INIT(); + CYG_TEST_INFO("Testing httpd"); + + cyg_thread_create( + 10, // Priority + main_thread_entry, // Entry + 0, // Entry parameter + "main", // Name + main_stack, // Stack + STACK_SIZE, // Size + &main_handle, // Handle + &main_thread // Thread data structure + ); + cyg_thread_resume(main_handle); + cyg_scheduler_start(); + + CYG_TEST_FAIL_FINISH("Not reached"); +} + +externC void +cyg_start(void) +{ + httpd_main(); +} + +#else // LWIP_TCP +#define N_A_MSG "TCP support disabled" +#endif // LWIP_TCP + +#else // CYGFUN_LWIP_MODE_SIMPLE +#define N_A_MSG "Not configured in 'Simple' mode" +#endif // CYGFUN_LWIP_MODE_SIMPLE + +#ifdef N_A_MSG +externC void +cyg_start(void) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG diff --git a/packages/net/lwip_tcpip/current/tests/nc_test_framework.h b/packages/net/lwip_tcpip/current/tests/nc_test_framework.h --- a/packages/net/lwip_tcpip/current/tests/nc_test_framework.h +++ b/packages/net/lwip_tcpip/current/tests/nc_test_framework.h @@ -5,22 +5,25 @@ // Network characterization tests framework // //========================================================================== -// ####BSDALTCOPYRIGHTBEGIN#### -// ------------------------------------------- -// Portions of this software may have been derived from FreeBSD, OpenBSD, -// or other sources, and if so are covered by the appropriate copyright -// and license included herein. -// ------------------------------------------- -// ####BSDALTCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD or other sources, +// and are covered by the appropriate copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): gthomas // Contributors: gthomas // Date: 2000-01-10 -// Purpose: -// Description: -// +// Purpose: +// Description: +// // //####DESCRIPTIONEND#### // @@ -29,7 +32,6 @@ #ifndef _TESTS_NC_TEST_FRAMEWORK_H_ #define _TESTS_NC_TEST_FRAMEWORK_H_ - #define test_printf diag_printf typedef cyg_addrword_t test_param_t; @@ -64,78 +66,71 @@ typedef cyg_addrword_t test_param_t; // been sent. // -#define MAX_ERRORS 5 // Give up after this many errors - -#define NC_REPLY_TIMEOUT 10 // The slave may be slow -#define NC_TEST_TIMEOUT 3 // More generous for tests -#define NC_RESULTS_TIMEOUT (MAX_ERRORS+2)*NC_TEST_TIMEOUT +#define MAX_ERRORS 5 // Give up after this many errors +#define NC_REPLY_TIMEOUT 10 // The slave may be slow +#define NC_TEST_TIMEOUT 3 // More generous for tests +#define NC_RESULTS_TIMEOUT (MAX_ERRORS+2)*NC_TEST_TIMEOUT struct nc_request { - int type; // Description of request - int seq; // Sequence number, used to build response - int nbufs; // Number of "buffers" to send - int buflen; // Length of each buffer - int slave_port; // Network ports to use + int type; // Description of request + int seq; // Sequence number, used to build response + int nbufs; // Number of "buffers" to send + int buflen; // Length of each buffer + int slave_port; // Network ports to use int master_port; - int timeout; // Max time to wait for any packet + int timeout; // Max time to wait for any packet }; -#define NC_REQUEST_DISCONNECT 0x0001 -#define NC_REQUEST_UDP_SEND 0x0010 // Slave to send UDP data -#define NC_REQUEST_UDP_RECV 0x0011 // Slave to receive UDP data -#define NC_REQUEST_UDP_ECHO 0x0012 // Master->slave->master -#define NC_REQUEST_TCP_SEND 0x0020 // Slave to send TCP data -#define NC_REQUEST_TCP_RECV 0x0021 // Slave to receive TCP data -#define NC_REQUEST_TCP_ECHO 0x0022 // Master->slave->master -#define NC_REQUEST_START_IDLE 0x0100 // Start some idle processing -#define NC_REQUEST_STOP_IDLE 0x0101 // Stop idle processing -#define NC_REQUEST_SET_LOAD 0x0200 // Set the background load level +#define NC_REQUEST_DISCONNECT 0x0001 +#define NC_REQUEST_UDP_SEND 0x0010 // Slave to send UDP data +#define NC_REQUEST_UDP_RECV 0x0011 // Slave to receive UDP data +#define NC_REQUEST_UDP_ECHO 0x0012 // Master->slave->master +#define NC_REQUEST_TCP_SEND 0x0020 // Slave to send TCP data +#define NC_REQUEST_TCP_RECV 0x0021 // Slave to receive TCP data +#define NC_REQUEST_TCP_ECHO 0x0022 // Master->slave->master +#define NC_REQUEST_START_IDLE 0x0100 // Start some idle processing +#define NC_REQUEST_STOP_IDLE 0x0101 // Stop idle processing +#define NC_REQUEST_SET_LOAD 0x0200 // Set the background load level struct nc_reply { - int response; // ACK or NAK - int seq; // Must match request - int reason; // If NAK, why request turned down - union { // Miscellaneous data, depending on request + int response; // ACK or NAK + int seq; // Must match request + int reason; // If NAK, why request turned down + union { // Miscellaneous data, depending on request struct { - long elapsed_time; // In 10ms "ticks" - long count[2]; // Result + long elapsed_time; // In 10ms "ticks" + long count[2]; // Result } idle_results; } misc; }; -#define NC_REPLY_ACK 0x0001 // Request accepted -#define NC_REPLY_NAK 0x0000 // Request denied - -#define NC_REPLY_NAK_UNKNOWN_REQUEST 0x0001 -#define NC_REPLY_NAK_BAD_REQUEST 0x0002 // Slave can't handle -#define NC_REPLY_NAK_NO_BACKGROUND 0x0003 // Slave can't do background/idle +#define NC_REPLY_ACK 0x0001 // Request accepted +#define NC_REPLY_NAK 0x0000 // Request denied +#define NC_REPLY_NAK_UNKNOWN_REQUEST 0x0001 +#define NC_REPLY_NAK_BAD_REQUEST 0x0002 // Slave can't handle +#define NC_REPLY_NAK_NO_BACKGROUND 0x0003 // Slave can't do background/idle -// // Test data 'packets' look like this - struct nc_test_data { - long key1; - int seq; - int len; - long key2; - char data[0]; // Actual data + long key1; + int seq; + int len; + long key2; + char data[0]; // Actual data }; #define NC_TEST_DATA_KEY1 0xC0DEADC0 #define NC_TEST_DATA_KEY2 0xC0DEADC1 struct nc_test_results { - long key1; // Identify uniquely as a response record - int seq; // Matches request - int nsent; - int nrecvd; - long key2; // Additional verification + long key1; // Identify uniquely as a response record + int seq; // Matches request + int nsent; + int nrecvd; + long key2; // Additional verification }; #define NC_TEST_RESULT_KEY1 0xDEADC0DE #define NC_TEST_RESULT_KEY2 0xDEADC1DE #endif // _TESTS_NC_TEST_FRAMEWORK_H_ - - - diff --git a/packages/net/lwip_tcpip/current/tests/nc_test_slave.c b/packages/net/lwip_tcpip/current/tests/nc_test_slave.c --- a/packages/net/lwip_tcpip/current/tests/nc_test_slave.c +++ b/packages/net/lwip_tcpip/current/tests/nc_test_slave.c @@ -5,13 +5,16 @@ // Network characterizations test (slave portion) // //========================================================================== -// ####BSDALTCOPYRIGHTBEGIN#### -// ------------------------------------------- -// Portions of this software may have been derived from FreeBSD, OpenBSD, -// or other sources, and if so are covered by the appropriate copyright -// and license included herein. -// ------------------------------------------- -// ####BSDALTCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD or other sources, +// and are covered by the appropriate copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // @@ -26,20 +29,23 @@ // //========================================================================== -// Network characterization test code - slave portion -#include #include - -#include "nc_test_framework.h" - #include +#include -#include -#include -#include -#define LWIP_TIMEVAL_PRIVATE 1 +#include + +#include #include -#include + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL +#if LWIP_SOCKET +#if LWIP_TCP +#if LWIP_UDP +#if LWIP_COMPAT_SOCKETS +#if LWIP_POSIX_SOCKETS_IO_NAMES +#if 1 // SO_REUSE + #ifndef CYGPKG_LIBC_STDIO #include @@ -47,6 +53,10 @@ #else #include #endif + +#include "nc_test_framework.h" + + #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) #define MAX_LOAD_THREAD_LEVEL 20 #define MIN_LOAD_THREAD_LEVEL 0 @@ -56,20 +66,8 @@ #define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-1 #define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-2 #define DESIRED_BACKGROUND_LOAD 20 -#define CYGHWR_NET_DRIVERS 1 -#if SO_REUSE -#ifdef CYGPKG_LWIP_TCP -#ifdef CYGPKG_LWIP_UDP - - -#if 0 -static char main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE]; -static cyg_thread main_thread_data[CYGHWR_NET_DRIVERS]; -static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS]; -#endif - - +static char main_thread_stack[STACK_SIZE]; static char idle_thread_stack[STACK_SIZE]; static cyg_thread idle_thread_data; static cyg_handle_t idle_thread_handle; @@ -188,13 +186,13 @@ do_udp_test(int s1, struct nc_request *r if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) && (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) { if (ntohl(tdp->seq) != seq) { - test_printf("Packets out of sequence - recvd: %d, expected: %d\n", + test_printf("Packets out of sequence - recvd: %lu, expected: %d\n", ntohl(tdp->seq), seq); seq = ntohl(tdp->seq); seq_errors++; } } else { - test_printf("Bad data packet - key: %lx/%lx, seq: %d\n", + test_printf("Bad data packet - key: %lx/%lx, seq: %lu\n", ntohl(tdp->key1), ntohl(tdp->key2), ntohl(tdp->seq)); } @@ -281,13 +279,13 @@ do_read(int fd, void *buf, int buflen) static void do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master) { - int i, s, len, td_len, seq, seq_errors, lost, test_chan, res; + int i, s, td_len, seq, seq_errors, lost, test_chan, res; struct sockaddr_in test_chan_slave, test_chan_master; + socklen_t len; struct nc_test_results results; struct nc_test_data *tdp; int nsent, nrecvd; int need_recv, need_send; - int one = 1; static int slave_tcp_port = -1; need_recv = true; need_send = true; @@ -309,15 +307,18 @@ do_tcp_test(int s1, struct nc_request *r if (s < 0) { pexit("datagram socket"); } - - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { - perror("setsockopt SO_REUSEADDR"); - return; - } -#ifdef SO_REUSEPORT - if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { - perror("setsockopt SO_REUSEPORT"); - return; +#if SO_REUSE + { + int one = 1; + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { + perror("setsockopt SO_REUSEADDR"); + return; + } + if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { + perror("setsockopt SO_REUSEPORT"); + return; + } } #endif memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave)); @@ -341,8 +342,8 @@ do_tcp_test(int s1, struct nc_request *r } len = sizeof(test_chan_master); getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len); -// test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr), - // ntohs(test_chan_master.sin_port)); + test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr), + ntohs(test_chan_master.sin_port)); nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0; for (i = 0; i < ntohl(req->nbufs); i++) { @@ -361,13 +362,13 @@ do_tcp_test(int s1, struct nc_request *r if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) && (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) { if (ntohl(tdp->seq) != seq) { - test_printf("Packets out of sequence - recvd: %d, expected: %d\n", + test_printf("Packets out of sequence - recvd: %lu, expected: %d\n", ntohl(tdp->seq), seq); seq = ntohl(tdp->seq); seq_errors++; } } else { - test_printf("Bad data packet - key: %lx/%lx, seq: %d\n", + test_printf("Bad data packet - key: %lx/%lx, seq: %lu\n", ntohl(tdp->key1), ntohl(tdp->key2), ntohl(tdp->seq)); } @@ -416,8 +417,9 @@ do_tcp_test(int s1, struct nc_request *r static void nc_slave(test_param_t param) { - int s, masterlen; + int s; struct sockaddr_in my_addr, master; + socklen_t masterlen; struct nc_request req; struct nc_reply reply; int done = false; @@ -444,10 +446,8 @@ nc_slave(test_param_t param) if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) { pexit("recvfrom"); } -#if 0 - test_printf("Request %d from %s:%d\n", ntohl(req.type), + test_printf("Request %lu from %s:%d\n", ntohl(req.type), inet_ntoa(master.sin_addr), ntohs(master.sin_port)); -#endif reply.response = htonl(NC_REPLY_ACK); reply.seq = req.seq; switch (ntohl(req.type)) { @@ -455,22 +455,22 @@ nc_slave(test_param_t param) done = true; break; case NC_REQUEST_UDP_SEND: - test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + test_printf("UDP send - %lu buffers, %lu bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); break; case NC_REQUEST_UDP_RECV: - test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + test_printf("UDP recv - %lu buffers, %lu bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); break; case NC_REQUEST_UDP_ECHO: - test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + test_printf("UDP echo - %lu buffers, %lu bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); break; case NC_REQUEST_TCP_SEND: - test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + test_printf("TCP send - %lu buffers, %lu bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); break; case NC_REQUEST_TCP_RECV: - test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + test_printf("TCP recv - %lu buffers, %lu bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); break; case NC_REQUEST_TCP_ECHO: - test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + test_printf("TCP echo - %lu buffers, %lu bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); break; case NC_REQUEST_SET_LOAD: start_load(ntohl(req.nbufs)); @@ -490,7 +490,7 @@ nc_slave(test_param_t param) reply.misc.idle_results.count[1] = htonl((long)idle_thread_count); break; default: - test_printf("Unrecognized request: %d\n", ntohl(req.type)); + test_printf("Unrecognized request: %lu\n", ntohl(req.type)); reply.response = htonl(NC_REPLY_NAK); reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST); break; @@ -557,18 +557,14 @@ calibrate_load(int desired_load) // Set limits high = MAX_LOAD_THREAD_LEVEL; low = MIN_LOAD_THREAD_LEVEL; - test_printf("Start Network Characterization - SLAVE\n"); // Compute the "no load" idle value idle_thread_count = 0; cyg_semaphore_post(&idle_thread_sem); // Start idle thread - test_printf("Start Network Characterization - SLAVE\n"); cyg_thread_delay(1*100); // Pause for one second - test_printf("Start Network Characterization - SLAVE\n"); cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread - test_printf("Start Network Characterization - SLAVE\n"); no_load_idle = idle_thread_count; - diag_printf("No load = %d\n", (int)idle_thread_count); + test_printf("No load = %d\n", (int)idle_thread_count); // First ensure that the HIGH level is indeed higher while (true) { @@ -581,7 +577,7 @@ calibrate_load(int desired_load) load_idle = idle_thread_count; start_load(0); // Shut down background load percent_load = 100 - ((load_idle * 100) / no_load_idle); - diag_printf("High Load[%ld] = %d => %d%%\n", load_thread_level, + test_printf("High Load[%ld] = %d => %d%%\n", load_thread_level, (int)idle_thread_count, percent_load); if ( percent_load > desired_load ) break; // HIGH level is indeed higher @@ -600,7 +596,7 @@ calibrate_load(int desired_load) load_idle = idle_thread_count; start_load(0); // Shut down background load percent_load = 100 - ((load_idle * 100) / no_load_idle); - diag_printf("Load[%ld] = %d => %d%%\n", load_thread_level, + test_printf("Load[%ld] = %d => %d%%\n", load_thread_level, (int)idle_thread_count, percent_load); if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break; if (percent_load < desired_load) { @@ -622,7 +618,7 @@ calibrate_load(int desired_load) load_idle = idle_thread_count; start_load(0); // Shut down background load percent_load = 100 - ((load_idle * 100) / no_load_idle); - diag_printf("Final load[%ld] = %d => %d%%\n", load_thread_level, + test_printf("Final load[%ld] = %d => %d%%\n", load_thread_level, (int)idle_thread_count, percent_load); // no_load_idle_count_1_second = no_load_idle; } @@ -699,24 +695,25 @@ net_idle(cyg_addrword_t param) cyg_semaphore_post(&idle_thread_sem); } } -#if 0 + +static char stack[STACK_SIZE]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + void -cyg_start(void *n) +tmain(cyg_addrword_t p) +{ + cyg_lwip_sequential_init(); + cyg_lwip_thread_new("main", net_test, NULL, main_thread_stack, STACK_SIZE, MAIN_THREAD_PRIORITY); +} + +externC void +cyg_start( void ) { int i; - // Create processing threads - for (i = 0; i < CYGHWR_NET_DRIVERS; i++) { - cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority - net_test, // entry - i, // entry parameter - "Network test", // Name - &main_thread_stack[i][0], // Stack - STACK_SIZE, // Size - &main_thread_handle[i], // Handle - &main_thread_data[i] // Thread data structure - ); - } - cyg_thread_resume(main_thread_handle[0]); // Start first one + + CYG_TEST_INIT(); + // Create the idle thread environment cyg_semaphore_init(&idle_thread_sem, 0); cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority @@ -727,10 +724,11 @@ cyg_start(void *n) STACK_SIZE, // Size &idle_thread_handle, // Handle &idle_thread_data // Thread data structure - ); + ); cyg_thread_resume(idle_thread_handle); // Start it + // Create the load threads and their environment(s) - for (i = 0; i < NUM_LOAD_THREADS; i++) { + for (i = 0; i < NUM_LOAD_THREADS; i++) { cyg_semaphore_init(&load_thread_sem[i], 0); cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority net_load, // entry @@ -740,89 +738,55 @@ cyg_start(void *n) STACK_SIZE, // Size &load_thread_handle[i], // Handle &load_thread_data[i] // Thread data structure - ); + ); cyg_thread_resume(load_thread_handle[i]); // Start it } - cyg_scheduler_start(); -} - -#endif - -void -tmain(cyg_addrword_t p) -{ - lwip_init(); - sys_thread_new(net_test, 0, MAIN_THREAD_PRIORITY); -} - -static char stack[STACK_SIZE]; -static cyg_thread thread_data; -static cyg_handle_t thread_handle; - -void -nc_slave_main(void) -{ - int i; - - CYG_TEST_INIT(); - // Create the idle thread environment - cyg_semaphore_init(&idle_thread_sem, 0); - cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority - net_idle, // entry + + // Create a main thread, so we can run the scheduler and have time 'pass' + cyg_thread_create(10, // Priority + tmain, // entry 0, // entry parameter - "Network idle", // Name - &idle_thread_stack[0], // Stack + "socket echo test", // Name + &stack[0], // Stack STACK_SIZE, // Size - &idle_thread_handle, // Handle - &idle_thread_data // Thread data structure + &thread_handle, // Handle + &thread_data // Thread data structure ); - cyg_thread_resume(idle_thread_handle); // Start it - // Create the load threads and their environment(s) - for (i = 0; i < NUM_LOAD_THREADS; i++) { - cyg_semaphore_init(&load_thread_sem[i], 0); - cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority - net_load, // entry - i, // entry parameter - "Background load", // Name - &load_thread_stack[i][0], // Stack - STACK_SIZE, // Size - &load_thread_handle[i], // Handle - &load_thread_data[i] // Thread data structure - ); - cyg_thread_resume(load_thread_handle[i]); // Start it - } - // Create a main thread, so we can run the scheduler and have time 'pass' - cyg_thread_create(10, // Priority - just a number - tmain, // entry - 0, // entry parameter - "socket echo test", // Name - &stack[0], // Stack - STACK_SIZE, // Size - &thread_handle, // Handle - &thread_data // Thread data structure - ); - cyg_thread_resume(thread_handle); // Start it + cyg_thread_resume(thread_handle); // Start it + cyg_scheduler_start(); + CYG_TEST_FAIL_FINISH("Not reached"); } - -externC void -cyg_start( void ) -{ - nc_slave_main(); -} - -#else // def CYGPKG_LWIP_UDP -#define N_A_MSG "UDP support disabled" -#endif // def CYGPKG_LWIP_UDP -#else // def CYGPKG_LWIP_TCP -#define N_A_MSG "TCP support disabled" -#endif // def CYGPKG_LWIP_TCP #else // SO_REUSE #define N_A_MSG "SO_REUSE currently unavailable" #endif // SO_REUSE +#else // LWIP_POSIX_SOCKETS_IO_NAMES +#define N_A_MSG "POSIX socket function names disabled" +#endif + +#else // LWIP_COMPAT_SOCKETS +#define N_A_MSG "Compatible socket support disabled" +#endif // LWIP_COMPAT_SOCKETS + +#else // LWIP_UDP +#define N_A_MSG "UDP support disabled" +#endif // LWIP_UDP + +#else // LWIP_TCP +#define N_A_MSG "TCP support disabled" +#endif // LWIP_TCP + +#else // LWIP_SOCKET +#define N_A_MSG "Socket support disabled" +#endif // LWIP_SOCKET + +#else // CYGFUN_LWIP_MODE_SEQUENTIAL +#define N_A_MSG "Not configured in 'Sequential' mode" +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL + #ifdef N_A_MSG externC void cyg_start( void ) diff --git a/packages/net/lwip_tcpip/current/tests/ppp.c b/packages/net/lwip_tcpip/current/tests/ppp.c new file mode 100644 --- /dev/null +++ b/packages/net/lwip_tcpip/current/tests/ppp.c @@ -0,0 +1,371 @@ +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== + +// Simple ppp test + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include "netif/ppp/ppp.h" +#include "netif/ppp/chat.h" + +#ifdef CYGFUN_LWIP_MODE_SIMPLE +#if PPP_SUPPORT +#if LWIP_TCP + +#define NUM_RUNS 5 +#define CHAT_TIMEOUT 30 + +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL + +static char main_stack[STACK_SIZE]; +static cyg_thread main_thread; +static cyg_handle_t main_handle; + +static struct chat_item chat_items[] = { + { CHAT_ABORT, "NO CARRIER" }, + { CHAT_ABORT, "NO DIALTONE" }, + { CHAT_ABORT, "BUSY" }, + { CHAT_ABORT, "ERROR" }, + { CHAT_SEND, "+++" }, + { CHAT_SLEEP, (char *) 2000 }, + { CHAT_SEND, "AT\r\n" }, + { CHAT_WAIT, "OK" }, + { CHAT_SEND, "ATZ\r\n" }, + { CHAT_WAIT, "OK" }, + { CHAT_SEND, "ATE0\r\n" }, + { CHAT_WAIT, "OK" }, + { CHAT_SEND_CB, (char *) 0 }, // AT+CGDCONT + { CHAT_WAIT, "OK" }, + { CHAT_SEND, (char *) 1 }, // ATD + { CHAT_WAIT, "CONNECT" }, + { CHAT_SLEEP, (char *) 1000 }, + { CHAT_LAST, 0 }, +}; + +enum test_state { + TEST_INITIAL, + TEST_CHAT_INIT, + TEST_CHAT_RUN, + TEST_CHAT_FINISH, + TEST_PPP_INIT, + TEST_PPP_INIT_WAIT, + TEST_PPP_UP, + TEST_PPP_CLOSE, + TEST_PPP_CLOSE_WAIT, + TEST_NEXT_RUN, + TEST_FINISH, +}; + +enum req_state { + REQ_DNS_INIT, + REQ_DNS_WAIT, + REQ_DNS_FAILED, + REQ_DNS_SUCCESS, +}; + +static enum test_state test_state; +static enum req_state req_state; +static sio_fd_t sd; +static chat_t chat; +static int pd; +static struct ip_addr host_addr; +static int run = 1; +static int success = 0; +static int chat_ok; + +static void +chat_cb(chat_t chat, chat_err_t err, void *arg) +{ + switch (err) { + case CHAT_ERR_OK: + CYG_TEST_INFO("Chat OK"); + chat_ok = 1; + break; + case CHAT_ERR_ABORT: + CYG_TEST_INFO("Chat aborted"); + chat_ok = 0; + break; + case CHAT_ERR_TIMEOUT: + CYG_TEST_INFO("Chat timeout"); + chat_ok = 0; + break; + } + + test_state = TEST_CHAT_FINISH; +} + +static void +chat_send_cb(chat_t chat, int id, char *buf, size_t len, void *arg) +{ + switch (id) { + case 0: + snprintf(buf, len, "AT+CGDCONT=1,\"IP\",\"%s\"\r\n", CYGDAT_NET_LWIP_PPP_TEST_APN); + break; + case 1: + snprintf(buf, len, "ATD%s\r\n", CYGDAT_NET_LWIP_PPP_TEST_NUMBER); + break; + } +} + +static void +ppp_status_cb(void *ctx, int err, void *arg) +{ + switch (err) { + case PPPERR_NONE: + CYG_TEST_INFO("PPP link up"); + // Setup DNS server + { + struct ppp_addrs *addrs = (struct ppp_addrs *) arg; + dns_setserver(2, &addrs->dns1); + } + + if (test_state == TEST_PPP_INIT_WAIT) + test_state = TEST_PPP_UP; + break; + case PPPERR_PARAM: + CYG_TEST_INFO("Invalid parameter"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case PPPERR_OPEN: + CYG_TEST_INFO("Unable to open PPP session"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case PPPERR_DEVICE: + CYG_TEST_INFO("Invalid I/O device for PPP"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case PPPERR_ALLOC: + CYG_TEST_INFO("Unable to allocate resources"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case PPPERR_USER: + CYG_TEST_INFO("User interrupt"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case PPPERR_CONNECT: + CYG_TEST_INFO("Connection lost"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case PPPERR_AUTHFAIL: + CYG_TEST_INFO("Failed authentication challenge"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case PPPERR_PROTOCOL: + CYG_TEST_INFO("Failed to meet protocol"); + test_state = TEST_PPP_CLOSE_WAIT; + break; + } +} + +static void +dns_found_cb(const char *name, struct ip_addr *addr, void *arg) +{ + if (addr) { + host_addr = *addr; + req_state = REQ_DNS_SUCCESS; + } else { + req_state = REQ_DNS_FAILED; + } +} + + +static void +handle_req_state(void) +{ + switch (req_state) { + case REQ_DNS_INIT: + CYG_TEST_INFO("Trying to resolve host name"); + if (dns_gethostbyname(CYGDAT_NET_LWIP_PPP_TEST_HOST, &host_addr, + dns_found_cb, NULL) == ERR_OK) { + // Cached + req_state = REQ_DNS_SUCCESS; + break; + } + req_state = REQ_DNS_WAIT; + break; + case REQ_DNS_WAIT: + break; + case REQ_DNS_FAILED: + CYG_TEST_INFO("Failed to resolve host name"); + test_state = TEST_PPP_CLOSE; + break; + case REQ_DNS_SUCCESS: + CYG_TEST_INFO("Successfully resolved host name"); + success++; + test_state = TEST_PPP_CLOSE; + break; + } +} + +void +main_thread_entry(cyg_addrword_t p) +{ + CYG_TEST_INFO("Initializing lwIP"); + cyg_lwip_simple_init(); + + test_state = TEST_CHAT_INIT; + req_state = REQ_DNS_INIT; + + while (1) { + cyg_lwip_simple_poll(); + switch (test_state) { + case TEST_INITIAL: + diag_printf("INFO:\n", run, NUM_RUNS); + test_state = TEST_CHAT_INIT; + break; + case TEST_CHAT_INIT: + CYG_TEST_INFO("Initializing chat"); + sd = sio_open(SIO_DEV_PPPOS); + if (!sd) + CYG_TEST_FAIL_FINISH("Cannot open serial"); + chat = chat_new(sd, chat_items, CHAT_TIMEOUT, chat_cb, chat_send_cb, NULL); + if (!chat) + CYG_TEST_FAIL_FINISH("Cannot allocate chat"); + test_state = TEST_CHAT_RUN; + break; + case TEST_CHAT_RUN: + chat_poll(chat); + break; + case TEST_CHAT_FINISH: + chat_free(chat); + if (chat_ok) + test_state = TEST_PPP_INIT; + else + test_state = TEST_NEXT_RUN; + break; + case TEST_PPP_INIT: + CYG_TEST_INFO("Initializing PPP"); + if (ppp_init() != ERR_OK) + CYG_TEST_FAIL_FINISH("Cannot initialize PPP"); + pd = ppp_open_serial(sd, ppp_status_cb, (void *) pd); + if (pd < 0) + CYG_TEST_FAIL_FINISH("Cannot open PPP over serial"); + ppp_set_auth(PPPAUTHTYPE_ANY, CYGDAT_NET_LWIP_PPP_TEST_USERNAME, + CYGDAT_NET_LWIP_PPP_TEST_PASSWORD); + test_state = TEST_PPP_INIT_WAIT; + break; + case TEST_PPP_INIT_WAIT: + ppp_poll(pd); + break; + case TEST_PPP_UP: + ppp_poll(pd); + handle_req_state(); + break; + case TEST_PPP_CLOSE: + ppp_close(pd); + test_state = TEST_PPP_CLOSE_WAIT; + break; + case TEST_PPP_CLOSE_WAIT: + ppp_poll(pd); + if (!ppp_is_open(pd)) + // Escape from data mode + sio_write(sd, (u8_t *) "+++", 3); + cyg_thread_delay(200); + test_state = TEST_NEXT_RUN; + break; + case TEST_NEXT_RUN: + if (run < NUM_RUNS) { + run++; + test_state = TEST_INITIAL; + } else { + test_state = TEST_FINISH; + } + break; + case TEST_FINISH: + test_state = TEST_CHAT_INIT; + diag_printf("INFO: -#undef LWIP_COMPAT_SOCKETS -#define LWIP_COMPAT_SOCKETS 1 -#include -#include +#include +#include #include -#ifdef CYGPKG_LWIP_TCP +#include + +#include +#include + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL +#if LWIP_SOCKET +#if LWIP_TCP +#if LWIP_COMPAT_SOCKETS +#if LWIP_POSIX_SOCKETS_IO_NAMES -char buf[400]; -static void -socket_thread(void *arg) +#define TCP_PORT 7 + +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL + +static char main_stack[STACK_SIZE]; +static cyg_thread main_thread; +static cyg_handle_t main_handle; + +static char socket_stack[STACK_SIZE]; + +static char buf[400]; + +static void socket_thread_entry(void *arg) { - int sock,s; - int len; - struct sockaddr_in addr,rem; - sock = socket(AF_INET,SOCK_STREAM,0); - addr.sin_family = AF_INET; - addr.sin_port = htons(7); - addr.sin_addr.s_addr = INADDR_ANY; + int sock, s; + int len; + struct sockaddr_in addr, rem; + socklen_t addrlen; + + sock = socket(AF_INET, SOCK_STREAM, 0); + addr.sin_family = AF_INET; + addr.sin_port = htons(TCP_PORT); + addr.sin_addr.s_addr = INADDR_ANY; - bind(sock,(struct sockaddr *)&addr,sizeof(addr)); - - listen(sock,5); - while(1) { - len = sizeof(rem); - s = accept(sock,(struct sockaddr*)&rem,&len); - while((len = read(s,buf,400)) > 0) - write(s,buf,len); - close(s); - } + bind(sock, (struct sockaddr *) &addr, sizeof(addr)); + + listen(sock, 5); + while (1) { + addrlen = sizeof(rem); + s = accept(sock, (struct sockaddr *) &rem, &addrlen); + while ((len = read(s, buf, 400)) > 0) + write(s, buf, len); + close(s); + } } -void -tmain(cyg_addrword_t p) +void main_thread_entry(cyg_addrword_t p) { - lwip_init(); - sys_thread_new(socket_thread, (void*)"socket",7); + cyg_lwip_sequential_init(); + cyg_lwip_thread_new("socket", socket_thread_entry, NULL, + socket_stack, STACK_SIZE, 7); } -#define STACK_SIZE 0x1000 -static char stack[STACK_SIZE]; -static cyg_thread thread_data; -static cyg_handle_t thread_handle; - -void -socket_main(void) +void socket_main(void) { CYG_TEST_INIT(); + CYG_TEST_INFO("Testing sockets"); + diag_printf("This test will echo TCP packets on local port %d\n", TCP_PORT); + // Create a main thread, so we can run the scheduler and have time 'pass' - cyg_thread_create(10, // Priority - just a number - tmain, // entry - 0, // entry parameter - "socket echo test", // Name - &stack[0], // Stack - STACK_SIZE, // Size - &thread_handle, // Handle - &thread_data // Thread data structure - ); - cyg_thread_resume(thread_handle); // Start it + cyg_thread_create( + 10, // Priority - just a number + main_thread_entry, // Entry + 0, // Entry parameter + "main", // Name + main_stack, // Stack + STACK_SIZE, // Size + &main_handle, // Handle + &main_thread // Thread data structure + ); + cyg_thread_resume(main_handle); cyg_scheduler_start(); + CYG_TEST_FAIL_FINISH("Not reached"); } -externC void -cyg_start( void ) +externC void cyg_start(void) { socket_main(); } -#else // def CYGPKG_LWIP_TCP +#else // LWIP_POSIX_SOCKETS_IO_NAMES +#define N_A_MSG "POSIX socket function names disabled" +#endif + +#else // LWIP_COMPAT_SOCKETS +#define N_A_MSG "Compatible socket support disabled" +#endif // LWIP_COMPAT_SOCKETS + +#else // LWIP_TCP #define N_A_MSG "TCP support disabled" -#endif // def CYGFUN_KERNEL_API_C +#endif // LWIP_TCP + +#else // LWIP_SOCKET +#define N_A_MSG "Socket support disabled" +#endif // LWIP_SOCKET + +#else // CYGFUN_LWIP_MODE_SEQUENTIAL +#define N_A_MSG "Not configured in 'Sequential' mode" +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL #ifdef N_A_MSG externC void -cyg_start( void ) +cyg_start(void) { CYG_TEST_INIT(); CYG_TEST_NA(N_A_MSG); } #endif // N_A_MSG - diff --git a/packages/net/lwip_tcpip/current/tests/sys_timeout.c b/packages/net/lwip_tcpip/current/tests/sys_timeout.c --- a/packages/net/lwip_tcpip/current/tests/sys_timeout.c +++ b/packages/net/lwip_tcpip/current/tests/sys_timeout.c @@ -1,97 +1,142 @@ //========================================================================== -// ####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2004 Free Software Foundation, Inc. +// +// sys_timeout.c +// +// Simple test-case for the lwip system timeout functionality. // -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later -// version. +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation // -// eCos is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. // -// You should have received a copy of the GNU General Public License -// along with eCos; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. // -// As a special exception, if other files instantiate templates or use -// macros or inline functions from this file, or you compile this file -// and link it with other works to produce a work based on this file, -// this file does not by itself cause the resulting work to be covered by -// the GNU General Public License. However the source code for this file -// must still be made available in accordance with section (3) of the GNU -// General Public License v2. +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### // -// This exception does not invalidate any other reasons why a work based -// on this file might be covered by the GNU General Public License. -// ------------------------------------------- -// ####ECOSGPLCOPYRIGHTEND#### +// Author(s): Simon Kallweit +// Contributors: +// Date: 2009-05-18 +// Purpose: +// Description: Simple test-case for the lwip system timeout functionality. +// +//####DESCRIPTIONEND#### +// //========================================================================== -/* Simple test-case for the lwip system timeout functionality */ +#include +#include +#include -#include "lwip/sys.h" +#include + +#include + +#define TIMERS 10 -#define TIMERS 100 -sys_timeout_handler timers[TIMERS]; -int intervals[TIMERS]; +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL +#if MEMP_NUM_SYS_TIMEOUT >= (TIMERS + 6) + +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL + +static char main_stack[STACK_SIZE]; +static cyg_thread main_thread; +static cyg_handle_t main_handle; + +static char timeout_stack[STACK_SIZE]; + +static int timeouts = 0; -static void -timeout_fn ( void * arg) +static void timeout_fn(void * arg) { - int i, j = (int)arg; -// diag_printf("Timeout #%d called\n", (int)arg); - for (i = 0; i < j; i++) - diag_printf(" "); - diag_printf("%d\n", j); -// sys_timeout( intervals[j], timers[j], arg); - sys_timeout( 1000, timers[j], arg); + int i = (int) arg; + + diag_printf("Timeout timer #%d\n", i); + + if (i != timeouts++) + CYG_TEST_FAIL_EXIT("Timeout out of order"); + + if (timeouts == TIMERS) + CYG_TEST_FINISH("Test successful"); +} + +static void timeout_thread_entry(void *arg) +{ + int i; + + for (i = 0; i < TIMERS; i++) { + diag_printf("Adding timer #%d\n", i); + sys_timeout((i + 1) * 100, timeout_fn, (void *) i); + } + sys_msleep(0); } -static void -timeout_thread(void *arg) +void main_thread_entry(cyg_addrword_t p) { - int i; - - for (i = 0; i < TIMERS; i++) { - diag_printf("Adding timer #%d\n", i); - timers[i] = timeout_fn; - intervals[i] = (i+1)*1000; - sys_timeout(intervals[i], timers[i], (void *)i); - } - sys_msleep(0); - diag_printf("Done\n"); -} - -void -tmain(cyg_addrword_t p) -{ - lwip_init(); - sys_thread_new(timeout_thread, (void*)"timeout",7); + cyg_lwip_sequential_init(); + cyg_lwip_thread_new("timeout", timeout_thread_entry, NULL, + timeout_stack, STACK_SIZE, 7); } -#define STACK_SIZE 0x1000 -static char stack[STACK_SIZE]; -static cyg_thread thread_data; -static cyg_handle_t thread_handle; +externC void +cyg_start(void) +{ + CYG_TEST_INIT(); + CYG_TEST_INFO("Testing timeouts"); -void -cyg_user_start(void) -{ // Create a main thread, so we can run the scheduler and have time 'pass' - cyg_thread_create(10, // Priority - just a number - tmain, // entry - 0, // entry parameter - "timeout test", // Name - &stack[0], // Stack - STACK_SIZE, // Size - &thread_handle, // Handle - &thread_data // Thread data structure - ); - cyg_thread_resume(thread_handle); // Start it + cyg_thread_create( + 10, // Priority - just a number + main_thread_entry, // Entry + 0, // Entry parameter + "main", // Name + main_stack, // Stack + STACK_SIZE, // Size + &main_handle, // Handle + &main_thread // Thread data structure + ); + cyg_thread_resume(main_handle); + cyg_scheduler_start(); + + CYG_TEST_FAIL_FINISH("Not reached"); } +#else // MEMP_NUM_SYS_TIMEOUT >= (TIMERS + 6) +#define N_A_MSG "Timeout memory pool is too small" +#endif // MEMP_NUM_SYS_TIMEOUT >= (TIMERS + 6) + +#else // CYGFUN_LWIP_MODE_SEQUENTIAL +#define N_A_MSG "Not configured in 'Sequential' mode" +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL + +#ifdef N_A_MSG +externC void +cyg_start(void) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG diff --git a/packages/net/lwip_tcpip/current/tests/tcpecho.c b/packages/net/lwip_tcpip/current/tests/tcpecho.c --- a/packages/net/lwip_tcpip/current/tests/tcpecho.c +++ b/packages/net/lwip_tcpip/current/tests/tcpecho.c @@ -1,129 +1,163 @@ -/* - * 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 - * - */ +//========================================================================== +// +// tcpecho.c +// +// Simple test-case for the netconn TCP API: echo on TCP port 7. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2009-05-18 +// Purpose: +// Description: Simple test-case for the netconn TCP API: echo on TCP port 7. +// +//####DESCRIPTIONEND#### +// +//========================================================================== -#include "lwip/sys.h" -#include "lwip/api.h" +#include +#include #include +#include -#ifdef CYGPKG_LWIP_TCP +#include -static void -tcpecho_thread(void *arg) -{ - struct netconn *conn, *newconn; - err_t err; +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL +#if LWIP_NETCONN +#if LWIP_TCP + +#define TCP_PORT 7 + +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL - /* Create a new connection identifier. */ - conn = netconn_new(NETCONN_TCP); +static char main_stack[STACK_SIZE]; +static cyg_thread main_thread; +static cyg_handle_t main_handle; + +static char tcpecho_stack[STACK_SIZE]; - /* Bind connection to well known port number 7. */ - netconn_bind(conn, NULL, 7); +static void tcpecho_thread_entry(void *arg) +{ + struct netconn *conn, *newconn; + err_t err; - /* Tell connection to go into listening mode. */ - netconn_listen(conn); + // Create a new connection identifier + conn = netconn_new(NETCONN_TCP); - while(1) { + // Bind connection to well known port number 7 + netconn_bind(conn, NULL, 7); - /* Grab new connection. */ - newconn = netconn_accept(conn); - /* Process the new connection. */ - if(newconn != NULL) { - struct netbuf *buf; - void *data; - u16_t len; - - while((buf = netconn_recv(newconn)) != NULL) { - do { - netbuf_data(buf, &data, &len); - err = netconn_write(newconn, data, len, NETCONN_COPY); - if(err != ERR_OK) { - } - } while(netbuf_next(buf) >= 0); - netbuf_delete(buf); - } - /* Close connection and discard connection identifier. */ - netconn_delete(newconn); + // Tell connection to go into listening mode + netconn_listen(conn); + + while (1) { + // Accept new connection + newconn = netconn_accept(conn); + + // Process new connection + if (newconn != NULL) { + struct netbuf *buf; + void *data; + u16_t len; + + while ((buf = netconn_recv(newconn)) != NULL) { + do { + netbuf_data(buf, &data, &len); + err = netconn_write(newconn, data, len, NETCONN_COPY); + if (err != ERR_OK) { + } + } while (netbuf_next(buf) >= 0); + netbuf_delete(buf); + } + + // Close connection and discard connection identifier + netconn_delete(newconn); + } } - } } -void -tmain(cyg_addrword_t p) +void main_thread_entry(cyg_addrword_t p) { - lwip_init(); - sys_thread_new(tcpecho_thread, (void*)"tcpecho",7); + cyg_lwip_sequential_init(); + cyg_lwip_thread_new("tcpecho", tcpecho_thread_entry, NULL, + tcpecho_stack, STACK_SIZE, 7); } -#define STACK_SIZE 0x1000 -static char stack[STACK_SIZE]; -static cyg_thread thread_data; -static cyg_handle_t thread_handle; - -void -tcpecho_main(void) +void tcpecho_main(void) { CYG_TEST_INIT(); - + CYG_TEST_INFO("Testing TCP"); + diag_printf("This test will echo TCP packets on local port %d\n", TCP_PORT); + // Create a main thread, so we can run the scheduler and have time 'pass' - cyg_thread_create(10, // Priority - just a number - tmain, // entry - 0, // entry parameter - "TCP echo test", // Name - &stack[0], // Stack - STACK_SIZE, // Size - &thread_handle, // Handle - &thread_data // Thread data structure - ); - - cyg_thread_resume(thread_handle); // Start it + cyg_thread_create( + 10, // Priority - just a number + main_thread_entry, // Entry + 0, // Entry parameter + "main", // Name + main_stack, // Stack + STACK_SIZE, // Size + &main_handle, // Handle + &main_thread // Thread data structure + ); + cyg_thread_resume(main_handle); cyg_scheduler_start(); + CYG_TEST_FAIL_FINISH("Not reached"); } -externC void -cyg_start( void ) +externC void cyg_start(void) { tcpecho_main(); } -#else // def CYGPKG_LWIP_TCP +#else // LWIP_TCP #define N_A_MSG "TCP support disabled" -#endif // def CYGFUN_KERNEL_API_C +#endif // LWIP_TCP + +#else // LWIP_NETCONN +#define N_A_MSG "NETCONN support disabled" +#endif // LWIP_NETCONN + +#else // CYGFUN_LWIP_MODE_SEQUENTIAL +#define N_A_MSG "Not configured in 'Sequential' mode" +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL #ifdef N_A_MSG externC void -cyg_start( void ) +cyg_start(void) { CYG_TEST_INIT(); CYG_TEST_NA(N_A_MSG); diff --git a/packages/net/lwip_tcpip/current/tests/udpecho.c b/packages/net/lwip_tcpip/current/tests/udpecho.c --- a/packages/net/lwip_tcpip/current/tests/udpecho.c +++ b/packages/net/lwip_tcpip/current/tests/udpecho.c @@ -1,94 +1,124 @@ -/* - * 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 - * - */ +//========================================================================== +// +// udpecho.c +// +// Simple test-case for the netconn UDP API: echo on UDP port 7. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Simon Kallweit +// Contributors: +// Date: 2009-05-18 +// Purpose: +// Description: Simple test-case for the netconn UDP API: echo on UDP port 7. +// +//####DESCRIPTIONEND#### +// +//========================================================================== -#include "lwip/api.h" -#include "lwip/sys.h" +#include +#include #include +#include + +#include + +#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL +#if LWIP_NETCONN +#if LWIP_UDP + +#define UDP_PORT 7 -#ifdef CYGPKG_LWIP_UDP +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL + +static char main_stack[STACK_SIZE]; +static cyg_thread main_thread; +static cyg_handle_t main_handle; -/*-----------------------------------------------------------------------------------*/ -char buffer[100]; -void -udpecho_thread(void *arg) +static char udpecho_stack[STACK_SIZE]; + +static char buffer[100]; + +void +udpecho_thread_entry(void *arg) { - struct netconn *conn; - struct netbuf *buf; - struct ip_addr *addr; - unsigned short port; - - conn = netconn_new(NETCONN_UDP); - netconn_bind(conn, NULL, 7); + struct netconn *conn; + struct netbuf *buf; + struct ip_addr *addr; + unsigned short port; + + conn = netconn_new(NETCONN_UDP); + netconn_bind(conn, NULL, UDP_PORT); - while(1) { - buf = netconn_recv(conn); - addr = netbuf_fromaddr(buf); - port = netbuf_fromport(buf); - netconn_connect(conn, addr, port); - netconn_send(conn, buf); - netbuf_copy(buf, buffer, sizeof(buffer)); - netbuf_delete(buf); - } -} -/*-----------------------------------------------------------------------------------*/ -void tmain(cyg_addrword_t p) -{ - lwip_init(); - sys_thread_new(udpecho_thread, (void*)"udpecho",7); + while (1) { + buf = netconn_recv(conn); + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + netconn_connect(conn, addr, port); + netconn_send(conn, buf); + netbuf_copy(buf, buffer, sizeof(buffer)); + netbuf_delete(buf); + } } -#define STACK_SIZE 0x1000 -static char stack[STACK_SIZE]; -static cyg_thread thread_data; -static cyg_handle_t thread_handle; +void +main_thread_entry(cyg_addrword_t p) +{ + cyg_lwip_sequential_init(); + cyg_lwip_thread_new("udpecho", udpecho_thread_entry, NULL, + udpecho_stack, STACK_SIZE, 7); +} void udpecho_main(void) { CYG_TEST_INIT(); + CYG_TEST_INFO("Testing UDP"); + diag_printf("This test will echo UDP packets on local port %d\n", UDP_PORT); + // Create a main thread, so we can run the scheduler and have time 'pass' - cyg_thread_create(10, // Priority - just a number - tmain, // entry - 0, // entry parameter - "UDP echo test", // Name - &stack[0], // Stack - STACK_SIZE, // Size - &thread_handle, // Handle - &thread_data // Thread data structure - ); - cyg_thread_resume(thread_handle); // Start it - + cyg_thread_create( + 10, // Priority - just a number + main_thread_entry, // Entry + 0, // Entry parameter + "main", // Name + main_stack, // Stack + STACK_SIZE, // Size + &main_handle, // Handle + &main_thread // Thread data structure + ); + cyg_thread_resume(main_handle); cyg_scheduler_start(); CYG_TEST_FAIL_FINISH("Not reached"); @@ -100,17 +130,23 @@ cyg_start( void ) udpecho_main(); } -#else // def CYGPKG_LWIP_UDP +#else // LWIP_UDP #define N_A_MSG "UDP support disabled" -#endif // def CYGFUN_KERNEL_API_C +#endif // LWIP_UDP + +#else // LWIP_NETCONN +#define N_A_MSG "NETCONN support disabled" +#endif // LWIP_NETCONN + +#else // CYGFUN_LWIP_MODE_SEQUENTIAL +#define N_A_MSG "Not configured in 'Sequential' mode" +#endif // CYGFUN_LWIP_MODE_SEQUENTIAL #ifdef N_A_MSG externC void -cyg_start( void ) +cyg_start(void) { CYG_TEST_INIT(); CYG_TEST_NA(N_A_MSG); } #endif // N_A_MSG - -