Introduction of sequential-API build variant, better thread safety (lwIP only)
This commit is contained in:
4295
ext/lwip/CHANGELOG
4295
ext/lwip/CHANGELOG
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2002 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 <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
src/ - The source code for the lwIP TCP/IP stack.
|
||||
doc/ - The documentation for lwIP.
|
||||
test/ - Some code to test whether the sources do what they should.
|
||||
|
||||
See also the FILES file in each subdirectory.
|
||||
100
ext/lwip/README
100
ext/lwip/README
@@ -1,100 +0,0 @@
|
||||
INTRODUCTION
|
||||
|
||||
lwIP is a small independent implementation of the TCP/IP protocol
|
||||
suite that has been developed by Adam Dunkels at the Computer and
|
||||
Networks Architectures (CNA) lab at the Swedish Institute of Computer
|
||||
Science (SICS).
|
||||
|
||||
The focus of the lwIP TCP/IP implementation is to reduce the RAM usage
|
||||
while still having a full scale TCP. This making lwIP suitable for use
|
||||
in embedded systems with tens of kilobytes of free RAM and room for
|
||||
around 40 kilobytes of code ROM.
|
||||
|
||||
|
||||
FEATURES
|
||||
|
||||
* IP (Internet Protocol, IPv4 and IPv6) including packet forwarding over
|
||||
multiple network interfaces
|
||||
* ICMP (Internet Control Message Protocol) for network maintenance and debugging
|
||||
* IGMP (Internet Group Management Protocol) for multicast traffic management
|
||||
* MLD (Multicast listener discovery for IPv6). Aims to be compliant with
|
||||
RFC 2710. No support for MLDv2
|
||||
* ND (Neighbor discovery and stateless address autoconfiguration for IPv6).
|
||||
Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
|
||||
(Address autoconfiguration)
|
||||
* UDP (User Datagram Protocol) including experimental UDP-lite extensions
|
||||
* TCP (Transmission Control Protocol) with congestion control, RTT estimation
|
||||
and fast recovery/fast retransmit
|
||||
* raw/native API for enhanced performance
|
||||
* Optional Berkeley-like socket API
|
||||
* DNS (Domain names resolver)
|
||||
|
||||
|
||||
APPLICATIONS
|
||||
|
||||
* HTTP server with SSI and CGI
|
||||
* SNMPv2c agent with MIB compiler (Simple Network Management Protocol)
|
||||
* SNTP (Simple network time protocol)
|
||||
* NetBIOS name service responder
|
||||
* MDNS (Multicast DNS) responder
|
||||
* iPerf server implementation
|
||||
|
||||
|
||||
LICENSE
|
||||
|
||||
lwIP is freely available under a BSD license.
|
||||
|
||||
|
||||
DEVELOPMENT
|
||||
|
||||
lwIP has grown into an excellent TCP/IP stack for embedded devices,
|
||||
and developers using the stack often submit bug fixes, improvements,
|
||||
and additions to the stack to further increase its usefulness.
|
||||
|
||||
Development of lwIP is hosted on Savannah, a central point for
|
||||
software development, maintenance and distribution. Everyone can
|
||||
help improve lwIP by use of Savannah's interface, Git and the
|
||||
mailing list. A core team of developers will commit changes to the
|
||||
Git source tree.
|
||||
|
||||
The lwIP TCP/IP stack is maintained in the 'lwip' Git module and
|
||||
contributions (such as platform ports) are in the 'contrib' Git module.
|
||||
|
||||
See doc/savannah.txt for details on Git server access for users and
|
||||
developers.
|
||||
|
||||
The current Git trees are web-browsable:
|
||||
http://git.savannah.gnu.org/cgit/lwip.git
|
||||
http://git.savannah.gnu.org/cgit/lwip/lwip-contrib.git
|
||||
|
||||
Submit patches and bugs via the lwIP project page:
|
||||
http://savannah.nongnu.org/projects/lwip/
|
||||
|
||||
Continuous integration builds (GCC, clang):
|
||||
https://travis-ci.org/yarrick/lwip-merged
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
|
||||
Self documentation of the source code is regularly extracted from the current
|
||||
Git sources and is available from this web page:
|
||||
http://www.nongnu.org/lwip/
|
||||
|
||||
There is now a constantly growing wiki about lwIP at
|
||||
http://lwip.wikia.com/wiki/LwIP_Wiki
|
||||
|
||||
Also, there are mailing lists you can subscribe at
|
||||
http://savannah.nongnu.org/mail/?group=lwip
|
||||
plus searchable archives:
|
||||
http://lists.nongnu.org/archive/html/lwip-users/
|
||||
http://lists.nongnu.org/archive/html/lwip-devel/
|
||||
|
||||
lwIP was originally written by Adam Dunkels:
|
||||
http://dunkels.com/adam/
|
||||
|
||||
Reading Adam's papers, the files in docs/, browsing the source code
|
||||
documentation and browsing the mailing list archives is a good way to
|
||||
become familiar with the design of lwIP.
|
||||
|
||||
Adam Dunkels <adam@sics.se>
|
||||
Leon Woestenberg <leon.woestenberg@gmx.net>
|
||||
@@ -1,243 +0,0 @@
|
||||
This file lists major changes between release versions that require
|
||||
ports or applications to be changed. Use it to update a port or an
|
||||
application written for an older version of lwIP to correctly work
|
||||
with newer versions.
|
||||
|
||||
|
||||
(git master)
|
||||
|
||||
* [Enter new changes just after this line - do not remove this line]
|
||||
|
||||
(2.0.2)
|
||||
|
||||
++ Application changes:
|
||||
|
||||
* slipif: The way to pass serial port number has changed. netif->num is not
|
||||
supported any more, netif->state is interpreted as an u8_t port number now
|
||||
(it's not a POINTER to an u8_t any more!)
|
||||
|
||||
(2.0.1)
|
||||
|
||||
++ Application changes:
|
||||
|
||||
* UDP does NOT receive multicast traffic from ALL netifs on an UDP PCB bound to a specific
|
||||
netif any more. Users need to bind to IP_ADDR_ANY to receive multicast traffic and compare
|
||||
ip_current_netif() to the desired netif for every packet.
|
||||
See bug #49662 for an explanation.
|
||||
|
||||
(2.0.0)
|
||||
|
||||
++ Application changes:
|
||||
|
||||
* Changed netif "up" flag handling to be an administrative flag (as opposed to the previous meaning of
|
||||
"ip4-address-valid", a netif will now not be used for transmission if not up) -> even a DHCP netif
|
||||
has to be set "up" before starting the DHCP client
|
||||
* Added IPv6 support (dual-stack or IPv4/IPv6 only)
|
||||
* Changed ip_addr_t to be a union in dual-stack mode (use ip4_addr_t where referring to IPv4 only).
|
||||
* Major rewrite of SNMP (added MIB parser that creates code stubs for custom MIBs);
|
||||
supports SNMPv2c (experimental v3 support)
|
||||
* Moved some core applications from contrib repository to src/apps (and include/lwip/apps)
|
||||
|
||||
+++ Raw API:
|
||||
* Changed TCP listen backlog: removed tcp_accepted(), added the function pair tcp_backlog_delayed()/
|
||||
tcp_backlog_accepted() to explicitly delay backlog handling on a connection pcb
|
||||
|
||||
+++ Socket API:
|
||||
* Added an implementation for posix sendmsg()
|
||||
* Added LWIP_FIONREAD_LINUXMODE that makes ioctl/FIONREAD return the size of the next pending datagram
|
||||
|
||||
++ Port changes
|
||||
|
||||
+++ new files:
|
||||
* MANY new and moved files!
|
||||
* Added src/Filelists.mk for use in Makefile projects
|
||||
* Continued moving stack-internal parts from abc.h to abc_priv.h in sub-folder "priv"
|
||||
to let abc.h only contain the actual application programmer's API
|
||||
|
||||
+++ sys layer:
|
||||
* Made LWIP_TCPIP_CORE_LOCKING==1 the default as it usually performs better than
|
||||
the traditional message passing (although with LWIP_COMPAT_MUTEX you are still
|
||||
open to priority inversion, so this is not recommended any more)
|
||||
* Added LWIP_NETCONN_SEM_PER_THREAD to use one "op_completed" semaphore per thread
|
||||
instead of using one per netconn (these semaphores are used even with core locking
|
||||
enabled as some longer lasting functions like big writes still need to delay)
|
||||
* Added generalized abstraction for itoa(), strnicmp(), stricmp() and strnstr()
|
||||
in def.h (to be overridden in cc.h) instead of config
|
||||
options for netbiosns, httpd, dns, etc. ...
|
||||
* New abstraction for hton* and ntoh* functions in def.h.
|
||||
To override them, use the following in cc.h:
|
||||
#define lwip_htons(x) <your_htons>
|
||||
#define lwip_htonl(x) <your_htonl>
|
||||
|
||||
+++ new options:
|
||||
* TODO
|
||||
|
||||
+++ new pools:
|
||||
* Added LWIP_MEMPOOL_* (declare/init/alloc/free) to declare private memp pools
|
||||
that share memp.c code but do not have to be made global via lwippools.h
|
||||
* Added pools for IPv6, MPU_COMPATIBLE, dns-api, netif-api, etc.
|
||||
* added hook LWIP_HOOK_MEMP_AVAILABLE() to get informed when a memp pool was empty and an item
|
||||
is now available
|
||||
|
||||
* Signature of LWIP_HOOK_VLAN_SET macro was changed
|
||||
|
||||
* LWIP_DECLARE_MEMORY_ALIGNED() may be used to declare aligned memory buffers (mem/memp)
|
||||
or to move buffers to dedicated memory using compiler attributes
|
||||
|
||||
* Standard C headers are used to define sized types and printf formatters
|
||||
(disable by setting LWIP_NO_STDINT_H=1 or LWIP_NO_INTTYPES_H=1 if your compiler
|
||||
does not support these)
|
||||
|
||||
|
||||
++ Major bugfixes/improvements
|
||||
|
||||
* Added IPv6 support (dual-stack or IPv4/IPv6 only)
|
||||
* Major rewrite of PPP (incl. keep-up with apache pppd)
|
||||
see doc/ppp.txt for an upgrading how-to
|
||||
* Major rewrite of SNMP (incl. MIB parser)
|
||||
* Fixed timing issues that might have lead to losing a DHCP lease
|
||||
* Made rx processing path more robust against crafted errors
|
||||
* TCP window scaling support
|
||||
* modification of api modules to support FreeRTOS-MPU (don't pass stack-pointers to other threads)
|
||||
* made DNS client more robust
|
||||
* support PBUF_REF for RX packets
|
||||
* LWIP_NETCONN_FULLDUPLEX allows netconn/sockets to be used for reading/writing from separate
|
||||
threads each (needs LWIP_NETCONN_SEM_PER_THREAD)
|
||||
* Moved and reordered stats (mainly memp/mib2)
|
||||
|
||||
(1.4.0)
|
||||
|
||||
++ Application changes:
|
||||
|
||||
* Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
|
||||
compatibility to old applications, but will be removed in the future).
|
||||
|
||||
* Renamed mem_realloc() to mem_trim() to prevent confusion with realloc()
|
||||
|
||||
+++ Raw API:
|
||||
* Changed the semantics of tcp_close() (since it was rather a
|
||||
shutdown before): Now the application does *NOT* get any calls to the recv
|
||||
callback (aside from NULL/closed) after calling tcp_close()
|
||||
|
||||
* When calling tcp_abort() from a raw API TCP callback function,
|
||||
make sure you return ERR_ABRT to prevent accessing unallocated memory.
|
||||
(ERR_ABRT now means the applicaiton has called tcp_abort!)
|
||||
|
||||
+++ Netconn API:
|
||||
* Changed netconn_receive() and netconn_accept() to return
|
||||
err_t, not a pointer to new data/netconn.
|
||||
|
||||
+++ Socket API:
|
||||
* LWIP_SO_RCVTIMEO: when accept() or recv() time out, they
|
||||
now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT.
|
||||
|
||||
* Added a minimal version of posix fctl() to have a
|
||||
standardised way to set O_NONBLOCK for nonblocking sockets.
|
||||
|
||||
+++ all APIs:
|
||||
* correctly implemented SO(F)_REUSEADDR
|
||||
|
||||
++ Port changes
|
||||
|
||||
+++ new files:
|
||||
|
||||
* Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h:
|
||||
|
||||
* Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains
|
||||
the actual application programmer's API
|
||||
|
||||
* Separated timer implementation from sys.h/.c, moved to timers.h/.c;
|
||||
Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you
|
||||
still want to use your own timer implementation for NO_SYS==0 (as before).
|
||||
|
||||
+++ sys layer:
|
||||
|
||||
* Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/
|
||||
sys_sem_t;
|
||||
|
||||
* Converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
|
||||
|
||||
* Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use
|
||||
binary semaphores instead of mutexes - as before)
|
||||
|
||||
+++ new options:
|
||||
|
||||
* Don't waste memory when chaining segments, added option TCP_OVERSIZE to
|
||||
prevent creating many small pbufs when calling tcp_write with many small
|
||||
blocks of data. Instead, pbufs are allocated larger than needed and the
|
||||
space is used for later calls to tcp_write.
|
||||
|
||||
* Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs
|
||||
in tcp_write/udp_send.
|
||||
|
||||
* Added an additional option LWIP_ETHERNET to support ethernet without ARP
|
||||
(necessary for pure PPPoE)
|
||||
|
||||
* Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may
|
||||
be used to place these pools into user-defined memory by using external
|
||||
declaration.
|
||||
|
||||
* Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT
|
||||
|
||||
+++ new pools:
|
||||
|
||||
* Netdb uses a memp pool for allocating memory when getaddrinfo() is called,
|
||||
so MEMP_NUM_NETDB has to be set accordingly.
|
||||
|
||||
* DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so
|
||||
MEMP_NUM_LOCALHOSTLIST has to be set accordingly.
|
||||
|
||||
* Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have
|
||||
to be set accordingly.
|
||||
|
||||
* PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES
|
||||
has to be set accordingly
|
||||
|
||||
* Integrated loopif into netif.c - loopif does not have to be created by the
|
||||
port any more, just define LWIP_HAVE_LOOPIF to 1.
|
||||
|
||||
* Added define LWIP_RAND() for lwip-wide randomization (needs to be defined
|
||||
in cc.h, e.g. used by igmp)
|
||||
|
||||
* Added printf-formatter X8_F to printf u8_t as hex
|
||||
|
||||
* The heap now may be moved to user-defined memory by defining
|
||||
LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
|
||||
|
||||
* added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work
|
||||
with user-allocated structs instead of calling mem_malloc
|
||||
|
||||
* Added const char* name to mem- and memp-stats for easier debugging.
|
||||
|
||||
* Calculate the TCP/UDP checksum while copying to only fetch data once:
|
||||
Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum
|
||||
|
||||
* Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to
|
||||
more than one pcb.
|
||||
|
||||
* Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned
|
||||
off any more, if this is set to 0, only one packet (the most recent one) is
|
||||
queued (like demanded by RFC 1122).
|
||||
|
||||
|
||||
++ Major bugfixes/improvements
|
||||
|
||||
* Implemented tcp_shutdown() to only shut down one end of a connection
|
||||
* Implemented shutdown() at socket- and netconn-level
|
||||
* Added errorset support to select() + improved select speed overhead
|
||||
* Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x)
|
||||
* Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1
|
||||
* Use macros defined in ip_addr.h to work with IP addresses
|
||||
* Implemented many nonblocking socket/netconn functions
|
||||
* Fixed ARP input processing: only add a new entry if a request was directed as us
|
||||
* mem_realloc() to mem_trim() to prevent confusion with realloc()
|
||||
* Some improvements for AutoIP (don't route/forward link-local addresses, don't break
|
||||
existing connections when assigning a routable address)
|
||||
* Correctly handle remote side overrunning our rcv_wnd in ooseq case
|
||||
* Removed packing from ip_addr_t, the packed version is now only used in protocol headers
|
||||
* Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0
|
||||
* Added support for static ARP table entries
|
||||
|
||||
(STABLE-1.3.2)
|
||||
|
||||
* initial version of this file
|
||||
@@ -1,9 +0,0 @@
|
||||
doxygen/ - Configuration files and scripts to create the lwIP doxygen source
|
||||
documentation (found at http://www.nongnu.org/lwip/)
|
||||
|
||||
savannah.txt - How to obtain the current development source code.
|
||||
contrib.txt - How to contribute to lwIP as a developer.
|
||||
rawapi.txt - The documentation for the core API of lwIP.
|
||||
Also provides an overview about the other APIs and multithreading.
|
||||
sys_arch.txt - The documentation for a system abstraction layer of lwIP.
|
||||
ppp.txt - Documentation of the PPP interface for lwIP.
|
||||
@@ -1,122 +0,0 @@
|
||||
void
|
||||
eth_mac_irq()
|
||||
{
|
||||
/* Service MAC IRQ here */
|
||||
|
||||
/* Allocate pbuf from pool (avoid using heap in interrupts) */
|
||||
struct pbuf* p = pbuf_alloc(PBUF_RAW, eth_data_count, PBUF_POOL);
|
||||
|
||||
if(p != NULL) {
|
||||
/* Copy ethernet frame into pbuf */
|
||||
pbuf_take(p, eth_data, eth_data_count);
|
||||
|
||||
/* Put in a queue which is processed in main loop */
|
||||
if(!queue_try_put(&queue, p)) {
|
||||
/* queue is full -> packet loss */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static err_t
|
||||
netif_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
/* Update SNMP stats (only if you use SNMP) */
|
||||
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
|
||||
int unicast = ((p->payload[0] & 0x01) == 0);
|
||||
if (unicast) {
|
||||
MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
|
||||
} else {
|
||||
MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
|
||||
}
|
||||
|
||||
lock_interrupts();
|
||||
pbuf_copy_partial(p, mac_send_buffer, p->tot_len, 0);
|
||||
/* Start MAC transmit here */
|
||||
unlock_interrupts();
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
netif_status_callback(struct netif *netif)
|
||||
{
|
||||
printf("netif status changed %s\n", ip4addr_ntoa(netif_ip4_addr(netif)));
|
||||
}
|
||||
|
||||
static err_t
|
||||
netif_init(struct netif *netif)
|
||||
{
|
||||
netif->linkoutput = netif_output;
|
||||
netif->output = etharp_output;
|
||||
netif->output_ip6 = ethip6_output;
|
||||
netif->mtu = ETHERNET_MTU;
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
|
||||
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 100000000);
|
||||
|
||||
SMEMCPY(netif->hwaddr, your_mac_address_goes_here, sizeof(netif->hwaddr));
|
||||
netif->hwaddr_len = sizeof(netif->hwaddr);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
struct netif netif;
|
||||
|
||||
lwip_init();
|
||||
|
||||
netif_add(&netif, IP4_ADDR_ANY, IP4_ADDR_ANY, IP4_ADDR_ANY, NULL, netif_init, netif_input);
|
||||
netif.name[0] = 'e';
|
||||
netif.name[1] = '0';
|
||||
netif_create_ip6_linklocal_address(&netif, 1);
|
||||
netif.ip6_autoconfig_enabled = 1;
|
||||
netif_set_status_callback(&netif, netif_status_callback);
|
||||
netif_set_default(&netif);
|
||||
netif_set_up(&netif);
|
||||
|
||||
/* Start DHCP and HTTPD */
|
||||
dhcp_start(&netif );
|
||||
httpd_init();
|
||||
|
||||
while(1) {
|
||||
/* Check link state, e.g. via MDIO communication with PHY */
|
||||
if(link_state_changed()) {
|
||||
if(link_is_up()) {
|
||||
netif_set_link_up(&netif);
|
||||
} else {
|
||||
netif_set_link_down(&netif);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for received frames, feed them to lwIP */
|
||||
lock_interrupts();
|
||||
struct pbuf* p = queue_try_get(&queue);
|
||||
unlock_interrupts();
|
||||
|
||||
if(p != NULL) {
|
||||
LINK_STATS_INC(link.recv);
|
||||
|
||||
/* Update SNMP stats (only if you use SNMP) */
|
||||
MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
|
||||
int unicast = ((p->payload[0] & 0x01) == 0);
|
||||
if (unicast) {
|
||||
MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
|
||||
} else {
|
||||
MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
|
||||
}
|
||||
|
||||
if(netif.input(p, &netif) != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cyclic lwIP timers check */
|
||||
sys_check_timeouts();
|
||||
|
||||
/* your application goes here */
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
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 Git 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 preferred 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. 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 Git access to submit and maintain your port in the contrib Git module.
|
||||
@@ -1 +0,0 @@
|
||||
doxygen lwip.Doxyfile
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
doxygen lwip.Doxyfile
|
||||
@@ -1,132 +0,0 @@
|
||||
/**
|
||||
* @defgroup lwip lwIP
|
||||
*
|
||||
* @defgroup infrastructure Infrastructure
|
||||
*
|
||||
* @defgroup callbackstyle_api Callback-style APIs
|
||||
* Non thread-safe APIs, callback style for maximum performance and minimum
|
||||
* memory footprint.
|
||||
*
|
||||
* @defgroup sequential_api Sequential-style APIs
|
||||
* Sequential-style APIs, blocking functions. More overhead, but can be called
|
||||
* from any thread except TCPIP thread.
|
||||
*
|
||||
* @defgroup addons Addons
|
||||
*
|
||||
* @defgroup apps Applications
|
||||
*/
|
||||
|
||||
/**
|
||||
* @mainpage Overview
|
||||
* @verbinclude "README"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page upgrading Upgrading
|
||||
* @verbinclude "UPGRADING"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page changelog Changelog
|
||||
* @verbinclude "CHANGELOG"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page contrib How to contribute to lwIP
|
||||
* @verbinclude "contrib.txt"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page pitfalls Common pitfalls
|
||||
*
|
||||
* Multiple Execution Contexts in lwIP code
|
||||
* ========================================
|
||||
*
|
||||
* The most common source of lwIP problems is to have multiple execution contexts
|
||||
* inside the lwIP code.
|
||||
*
|
||||
* lwIP can be used in two basic modes: @ref lwip_nosys (no OS/RTOS
|
||||
* running on target system) or @ref lwip_os (there is an OS running
|
||||
* on the target system).
|
||||
*
|
||||
* Mainloop Mode
|
||||
* -------------
|
||||
* In mainloop mode, only @ref callbackstyle_api can be used.
|
||||
* The user has two possibilities to ensure there is only one
|
||||
* exection context at a time in lwIP:
|
||||
*
|
||||
* 1) Deliver RX ethernet packets directly in interrupt context to lwIP
|
||||
* by calling netif->input directly in interrupt. This implies all lwIP
|
||||
* callback functions are called in IRQ context, which may cause further
|
||||
* problems in application code: IRQ is blocked for a long time, multiple
|
||||
* execution contexts in application code etc. When the application wants
|
||||
* to call lwIP, it only needs to disable interrupts during the call.
|
||||
* If timers are involved, even more locking code is needed to lock out
|
||||
* timer IRQ and ethernet IRQ from each other, assuming these may be nested.
|
||||
*
|
||||
* 2) Run lwIP in a mainloop. There is example code here: @ref lwip_nosys.
|
||||
* lwIP is _ONLY_ called from mainloop callstacks here. The ethernet IRQ
|
||||
* has to put received telegrams into a queue which is polled in the
|
||||
* mainloop. Ensure lwIP is _NEVER_ called from an interrupt, e.g.
|
||||
* some SPI IRQ wants to forward data to udp_send() or tcp_write()!
|
||||
*
|
||||
* OS Mode
|
||||
* -------
|
||||
* In OS mode, @ref callbackstyle_api AND @ref sequential_api can be used.
|
||||
* @ref sequential_api are designed to be called from threads other than
|
||||
* the TCPIP thread, so there is nothing to consider here.
|
||||
* But @ref callbackstyle_api functions must _ONLY_ be called from
|
||||
* TCPIP thread. It is a common error to call these from other threads
|
||||
* or from IRQ contexts. Ethernet RX needs to deliver incoming packets
|
||||
* in the correct way by sending a message to TCPIP thread, this is
|
||||
* implemented in tcpip_input().
|
||||
* Again, ensure lwIP is _NEVER_ called from an interrupt, e.g.
|
||||
* some SPI IRQ wants to forward data to udp_send() or tcp_write()!
|
||||
*
|
||||
* 1) tcpip_callback() can be used get called back from TCPIP thread,
|
||||
* it is safe to call any @ref callbackstyle_api from there.
|
||||
*
|
||||
* 2) Use @ref LWIP_TCPIP_CORE_LOCKING. All @ref callbackstyle_api
|
||||
* functions can be called when lwIP core lock is aquired, see
|
||||
* @ref LOCK_TCPIP_CORE() and @ref UNLOCK_TCPIP_CORE().
|
||||
* These macros cannot be used in an interrupt context!
|
||||
* Note the OS must correctly handle priority inversion for this.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page bugs Reporting bugs
|
||||
* Please report bugs in the lwIP bug tracker at savannah.\n
|
||||
* BEFORE submitting, please check if the bug has already been reported!\n
|
||||
* https://savannah.nongnu.org/bugs/?group=lwip
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup lwip_nosys Mainloop mode ("NO_SYS")
|
||||
* @ingroup lwip
|
||||
* Use this mode if you do not run an OS on your system. \#define NO_SYS to 1.
|
||||
* Feed incoming packets to netif->input(pbuf, netif) function from mainloop,
|
||||
* *not* *from* *interrupt* *context*. You can allocate a @ref pbuf in interrupt
|
||||
* context and put them into a queue which is processed from mainloop.\n
|
||||
* Call sys_check_timeouts() periodically in the mainloop.\n
|
||||
* Porting: implement all functions in @ref sys_time, @ref sys_prot and
|
||||
* @ref compiler_abstraction.\n
|
||||
* You can only use @ref callbackstyle_api in this mode.\n
|
||||
* Sample code:\n
|
||||
* @include NO_SYS_SampleCode.c
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup lwip_os OS mode (TCPIP thread)
|
||||
* @ingroup lwip
|
||||
* Use this mode if you run an OS on your system. It is recommended to
|
||||
* use an RTOS that correctly handles priority inversion and
|
||||
* to use @ref LWIP_TCPIP_CORE_LOCKING.\n
|
||||
* Porting: implement all functions in @ref sys_layer.\n
|
||||
* You can use @ref callbackstyle_api together with @ref tcpip_callback,
|
||||
* and all @ref sequential_api.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page raw_api lwIP API
|
||||
* @verbinclude "rawapi.txt"
|
||||
*/
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Redirection</title>
|
||||
<meta http-equiv="refresh" content="0; url=html/index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<a href="html/index.html">index.html</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,113 +0,0 @@
|
||||
Multicast DNS for lwIP
|
||||
|
||||
Author: Erik Ekman
|
||||
|
||||
|
||||
Note! The MDNS responder does not have all features required by the standards.
|
||||
See notes in src/apps/mdns/mdns.c for what is left. It is however usable in normal
|
||||
cases - but watch out if many devices on the same network try to use the same
|
||||
host/service instance names.
|
||||
|
||||
|
||||
How to enable:
|
||||
==============
|
||||
|
||||
MDNS support does not depend on DNS.
|
||||
MDNS supports using IPv4 only, v6 only, or v4+v6.
|
||||
|
||||
To enable MDNS responder, set
|
||||
LWIP_MDNS_RESPONDER = 1
|
||||
in lwipopts.h and add src/apps/mdns/mdns.c to your list of files to build.
|
||||
|
||||
The max number of services supported per netif is defined by MDNS_MAX_SERVICES,
|
||||
default is 1.
|
||||
|
||||
Increase MEMP_NUM_UDP_PCB by 1. MDNS needs one PCB.
|
||||
Increase LWIP_NUM_NETIF_CLIENT_DATA by 1 (MDNS needs one entry on netif).
|
||||
|
||||
MDNS with IPv4 requires LWIP_IGMP = 1, and preferably LWIP_AUTOIP = 1.
|
||||
MDNS with IPv6 requires LWIP_IPV6_MLD = 1, and that a link-local address is
|
||||
generated.
|
||||
|
||||
The MDNS code puts its structs on the stack where suitable to reduce dynamic
|
||||
memory allocation. It may use up to 1kB of stack.
|
||||
|
||||
MDNS needs a strncasecmp() implementation. If you have one, define
|
||||
LWIP_MDNS_STRNCASECMP to it. Otherwise the code will provide an implementation
|
||||
for you.
|
||||
|
||||
|
||||
How to use:
|
||||
===========
|
||||
|
||||
Call mdns_resp_init() during system initialization.
|
||||
This opens UDP sockets on port 5353 for IPv4 and IPv6.
|
||||
|
||||
|
||||
To start responding on a netif, run
|
||||
mdns_resp_add_netif(struct netif *netif, char *hostname, u32_t dns_ttl)
|
||||
|
||||
The hostname will be copied. If this returns successfully, the netif will join
|
||||
the multicast groups and any MDNS/legacy DNS requests sent unicast or multicast
|
||||
to port 5353 will be handled:
|
||||
- <hostname>.local type A, AAAA or ANY returns relevant IP addresses
|
||||
- Reverse lookups (PTR in-addr.arpa, ip6.arpa) of netif addresses
|
||||
returns <hostname>.local
|
||||
Answers will use the supplied TTL (in seconds)
|
||||
MDNS allows UTF-8 names, but it is recommended to stay within ASCII,
|
||||
since the default case-insensitive comparison assumes this.
|
||||
|
||||
It is recommended to call this function after an IPv4 address has been set,
|
||||
since there is currently no check if the v4 address is valid.
|
||||
|
||||
Call mdns_resp_netif_settings_changed() every time the IP address
|
||||
on the netif has changed.
|
||||
|
||||
To stop responding on a netif, run
|
||||
mdns_resp_remove_netif(struct netif *netif)
|
||||
|
||||
|
||||
Adding services:
|
||||
================
|
||||
|
||||
The netif first needs to be registered. Then run
|
||||
mdns_resp_add_service(struct netif *netif, char *name, char *service,
|
||||
u16_t proto, u16_t port, u32_t dns_ttl,
|
||||
service_get_txt_fn_t txt_fn, void *txt_userdata);
|
||||
|
||||
The name and service pointers will be copied. Name refers to the name of the
|
||||
service instance, and service is the type of service, like _http
|
||||
proto can be DNSSD_PROTO_UDP or DNSSD_PROTO_TCP which represent _udp and _tcp.
|
||||
If this call returns successfully, the following queries will be answered:
|
||||
- _services._dns-sd._udp.local type PTR returns <service>.<proto>.local
|
||||
- <service>.<proto>.local type PTR returns <name>.<service>.<proto>.local
|
||||
- <name>.<service>.<proto>.local type SRV returns hostname and port of service
|
||||
- <name>.<service>.<proto>.local type TXT builds text strings by calling txt_fn
|
||||
with the supplied userdata. The callback adds strings to the reply by calling
|
||||
mdns_resp_add_service_txtitem(struct mdns_service *service, char *txt,
|
||||
int txt_len). Example callback method:
|
||||
|
||||
static void srv_txt(struct mdns_service *service, void *txt_userdata)
|
||||
{
|
||||
res = mdns_resp_add_service_txtitem(service, "path=/", 6);
|
||||
LWIP_ERROR("mdns add service txt failed\n", (res == ERR_OK), return);
|
||||
}
|
||||
|
||||
Since a hostname struct is used for TXT storage each single item can be max
|
||||
63 bytes long, and the total max length (including length bytes for each
|
||||
item) is 255 bytes.
|
||||
|
||||
If your device runs a webserver on port 80, an example call might be:
|
||||
|
||||
mdns_resp_add_service(netif, "myweb", "_http"
|
||||
DNSSD_PROTO_TCP, 80, 3600, srv_txt, NULL);
|
||||
|
||||
which will publish myweb._http._tcp.local for any hosts looking for web servers,
|
||||
and point them to <hostname>.local:80
|
||||
|
||||
Relevant information will be sent as additional records to reduce number of
|
||||
requests required from a client.
|
||||
|
||||
Removing services is currently not supported. Services are removed when the
|
||||
netif is removed.
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
MQTT client for lwIP
|
||||
|
||||
Author: Erik Andersson
|
||||
|
||||
Details of the MQTT protocol can be found at:
|
||||
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
|
||||
|
||||
-----------------------------------------------------------------
|
||||
1. Initial steps, reserve memory and make connection to server:
|
||||
|
||||
1.1: Provide storage
|
||||
|
||||
Static allocation:
|
||||
mqtt_client_t static_client;
|
||||
example_do_connect(&static_client);
|
||||
|
||||
Dynamic allocation:
|
||||
mqtt_client_t *client = mqtt_client_new();
|
||||
if(client != NULL) {
|
||||
example_do_connect(&client);
|
||||
}
|
||||
|
||||
1.2: Establish Connection with server
|
||||
|
||||
void example_do_connect(mqtt_client_t *client)
|
||||
{
|
||||
struct mqtt_connect_client_info_t ci;
|
||||
err_t err;
|
||||
|
||||
/* Setup an empty client info structure */
|
||||
memset(&ci, 0, sizeof(ci));
|
||||
|
||||
/* Minimal amount of information required is client identifier, so set it here */
|
||||
ci.client_id = "lwip_test";
|
||||
|
||||
/* Initiate client and connect to server, if this fails immediately an error code is returned
|
||||
otherwise mqtt_connection_cb will be called with connection result after attempting
|
||||
to establish a connection with the server.
|
||||
For now MQTT version 3.1.1 is always used */
|
||||
|
||||
err = mqtt_client_connect(client, ip_addr, MQTT_PORT, mqtt_connection_cb, 0, &ci);
|
||||
|
||||
/* For now just print the result code if something goes wrong
|
||||
if(err != ERR_OK) {
|
||||
printf("mqtt_connect return %d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
Connection to server can also be probed by calling mqtt_client_is_connected(client)
|
||||
|
||||
-----------------------------------------------------------------
|
||||
2. Implementing the connection status callback
|
||||
|
||||
|
||||
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
|
||||
{
|
||||
err_t err;
|
||||
if(status == MQTT_CONNECT_ACCEPTED) {
|
||||
printf("mqtt_connection_cb: Successfully connected\n");
|
||||
|
||||
/* Setup callback for incoming publish requests */
|
||||
mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
|
||||
|
||||
/* Subscribe to a topic named "subtopic" with QoS level 1, call mqtt_sub_request_cb with result */
|
||||
err = mqtt_subscribe(client, "subtopic", 1, mqtt_sub_request_cb, arg);
|
||||
|
||||
if(err != ERR_OK) {
|
||||
printf("mqtt_subscribe return: %d\n", err);
|
||||
}
|
||||
} else {
|
||||
printf("mqtt_connection_cb: Disconnected, reason: %d\n", status);
|
||||
|
||||
/* Its more nice to be connected, so try to reconnect */
|
||||
example_do_connect(client);
|
||||
}
|
||||
}
|
||||
|
||||
static void mqtt_sub_request_cb(void *arg, err_t result)
|
||||
{
|
||||
/* Just print the result code here for simplicity,
|
||||
normal behaviour would be to take some action if subscribe fails like
|
||||
notifying user, retry subscribe or disconnect from server */
|
||||
printf("Subscribe result: %d\n", result);
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------
|
||||
3. Implementing callbacks for incoming publish and data
|
||||
|
||||
/* The idea is to demultiplex topic and create some reference to be used in data callbacks
|
||||
Example here uses a global variable, better would be to use a member in arg
|
||||
If RAM and CPU budget allows it, the easiest implementation might be to just take a copy of
|
||||
the topic string and use it in mqtt_incoming_data_cb
|
||||
*/
|
||||
static int inpub_id;
|
||||
static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
|
||||
{
|
||||
printf("Incoming publish at topic %s with total length %u\n", topic, (unsigned int)tot_len);
|
||||
|
||||
/* Decode topic string into a user defined reference */
|
||||
if(strcmp(topic, "print_payload") == 0) {
|
||||
inpub_id = 0;
|
||||
} else if(topic[0] == 'A') {
|
||||
/* All topics starting with 'A' might be handled at the same way */
|
||||
inpub_id = 1;
|
||||
} else {
|
||||
/* For all other topics */
|
||||
inpub_id = 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
|
||||
{
|
||||
printf("Incoming publish payload with length %d, flags %u\n", len, (unsigned int)flags);
|
||||
|
||||
if(flags & MQTT_DATA_FLAG_LAST) {
|
||||
/* Last fragment of payload received (or whole part if payload fits receive buffer
|
||||
See MQTT_VAR_HEADER_BUFFER_LEN) */
|
||||
|
||||
/* Call function or do action depending on reference, in this case inpub_id */
|
||||
if(inpub_id == 0) {
|
||||
/* Don't trust the publisher, check zero termination */
|
||||
if(data[len-1] == 0) {
|
||||
printf("mqtt_incoming_data_cb: %s\n", (const char *)data);
|
||||
}
|
||||
} else if(inpub_id == 1) {
|
||||
/* Call an 'A' function... */
|
||||
} else {
|
||||
printf("mqtt_incoming_data_cb: Ignoring payload...\n");
|
||||
}
|
||||
} else {
|
||||
/* Handle fragmented payload, store in buffer, write to file or whatever */
|
||||
}
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------
|
||||
4. Using outgoing publish
|
||||
|
||||
|
||||
void example_publish(mqtt_client_t *client, void *arg)
|
||||
{
|
||||
const char *pub_payload= "PubSubHubLubJub";
|
||||
err_t err;
|
||||
u8_t qos = 2; /* 0 1 or 2, see MQTT specification */
|
||||
u8_t retain = 0; /* No don't retain such crappy payload... */
|
||||
err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
|
||||
if(err != ERR_OK) {
|
||||
printf("Publish err: %d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when publish is complete either with sucess or failure */
|
||||
static void mqtt_pub_request_cb(void *arg, err_t result)
|
||||
{
|
||||
if(result != ERR_OK) {
|
||||
printf("Publish result: %d\n", result);
|
||||
}
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------
|
||||
5. Disconnecting
|
||||
|
||||
Simply call mqtt_disconnect(client)
|
||||
@@ -1,529 +0,0 @@
|
||||
PPP interface for lwIP
|
||||
|
||||
Author: Sylvain Rochet
|
||||
|
||||
Table of Contents:
|
||||
|
||||
1 - Supported PPP protocols and features
|
||||
2 - Raw API PPP example for all protocols
|
||||
3 - PPPoS input path (raw API, IRQ safe API, TCPIP API)
|
||||
4 - Thread safe PPP API (PPPAPI)
|
||||
5 - Notify phase callback (PPP_NOTIFY_PHASE)
|
||||
6 - Upgrading from lwIP <= 1.4.x to lwIP >= 2.0.x
|
||||
|
||||
|
||||
|
||||
1 Supported PPP protocols and features
|
||||
======================================
|
||||
|
||||
Supported Low level protocols:
|
||||
* PPP over serial using HDLC-like framing, such as wired dialup modems
|
||||
or mobile telecommunications GPRS/EDGE/UMTS/HSPA+/LTE modems
|
||||
* PPP over Ethernet, such as xDSL modems
|
||||
* PPP over L2TP (Layer 2 Tunneling Protocol) LAC (L2TP Access Concentrator),
|
||||
IP tunnel over UDP, such as VPN access
|
||||
|
||||
Supported auth protocols:
|
||||
* PAP, Password Authentication Protocol
|
||||
* CHAP, Challenge-Handshake Authentication Protocol, also known as CHAP-MD5
|
||||
* MSCHAPv1, Microsoft version of CHAP, version 1
|
||||
* MSCHAPv2, Microsoft version of CHAP, version 2
|
||||
* EAP, Extensible Authentication Protocol
|
||||
|
||||
Supported address protocols:
|
||||
* IPCP, IP Control Protocol, IPv4 addresses negotiation
|
||||
* IP6CP, IPv6 Control Protocol, IPv6 link-local addresses negotiation
|
||||
|
||||
Supported encryption protocols:
|
||||
* MPPE, Microsoft Point-to-Point Encryption
|
||||
|
||||
Supported compression or miscellaneous protocols, for serial links only:
|
||||
* PFC, Protocol Field Compression
|
||||
* ACFC, Address-and-Control-Field-Compression
|
||||
* ACCM, Asynchronous-Control-Character-Map
|
||||
* VJ, Van Jacobson TCP/IP Header Compression
|
||||
|
||||
|
||||
|
||||
2 Raw API PPP example for all protocols
|
||||
=======================================
|
||||
|
||||
As usual, raw API for lwIP means the lightweight API which *MUST* only be used
|
||||
for NO_SYS=1 systems or called inside lwIP core thread for NO_SYS=0 systems.
|
||||
|
||||
/*
|
||||
* Globals
|
||||
* =======
|
||||
*/
|
||||
|
||||
/* The PPP control block */
|
||||
ppp_pcb *ppp;
|
||||
|
||||
/* The PPP IP interface */
|
||||
struct netif ppp_netif;
|
||||
|
||||
|
||||
/*
|
||||
* PPP status callback
|
||||
* ===================
|
||||
*
|
||||
* PPP status callback is called on PPP status change (up, down, …) from lwIP
|
||||
* core thread
|
||||
*/
|
||||
|
||||
/* PPP status callback example */
|
||||
static void status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
|
||||
struct netif *pppif = ppp_netif(pcb);
|
||||
LWIP_UNUSED_ARG(ctx);
|
||||
|
||||
switch(err_code) {
|
||||
case PPPERR_NONE: {
|
||||
#if LWIP_DNS
|
||||
const ip_addr_t *ns;
|
||||
#endif /* LWIP_DNS */
|
||||
printf("status_cb: Connected\n");
|
||||
#if PPP_IPV4_SUPPORT
|
||||
printf(" our_ipaddr = %s\n", ipaddr_ntoa(&pppif->ip_addr));
|
||||
printf(" his_ipaddr = %s\n", ipaddr_ntoa(&pppif->gw));
|
||||
printf(" netmask = %s\n", ipaddr_ntoa(&pppif->netmask));
|
||||
#if LWIP_DNS
|
||||
ns = dns_getserver(0);
|
||||
printf(" dns1 = %s\n", ipaddr_ntoa(ns));
|
||||
ns = dns_getserver(1);
|
||||
printf(" dns2 = %s\n", ipaddr_ntoa(ns));
|
||||
#endif /* LWIP_DNS */
|
||||
#endif /* PPP_IPV4_SUPPORT */
|
||||
#if PPP_IPV6_SUPPORT
|
||||
printf(" our6_ipaddr = %s\n", ip6addr_ntoa(netif_ip6_addr(pppif, 0)));
|
||||
#endif /* PPP_IPV6_SUPPORT */
|
||||
break;
|
||||
}
|
||||
case PPPERR_PARAM: {
|
||||
printf("status_cb: Invalid parameter\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_OPEN: {
|
||||
printf("status_cb: Unable to open PPP session\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_DEVICE: {
|
||||
printf("status_cb: Invalid I/O device for PPP\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_ALLOC: {
|
||||
printf("status_cb: Unable to allocate resources\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_USER: {
|
||||
printf("status_cb: User interrupt\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_CONNECT: {
|
||||
printf("status_cb: Connection lost\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_AUTHFAIL: {
|
||||
printf("status_cb: Failed authentication challenge\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_PROTOCOL: {
|
||||
printf("status_cb: Failed to meet protocol\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_PEERDEAD: {
|
||||
printf("status_cb: Connection timeout\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_IDLETIMEOUT: {
|
||||
printf("status_cb: Idle Timeout\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_CONNECTTIME: {
|
||||
printf("status_cb: Max connect time reached\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_LOOPBACK: {
|
||||
printf("status_cb: Loopback detected\n");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
printf("status_cb: Unknown error code %d\n", err_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This should be in the switch case, this is put outside of the switch
|
||||
* case for example readability.
|
||||
*/
|
||||
|
||||
if (err_code == PPPERR_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* ppp_close() was previously called, don't reconnect */
|
||||
if (err_code == PPPERR_USER) {
|
||||
/* ppp_free(); -- can be called here */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to reconnect in 30 seconds, if you need a modem chatscript you have
|
||||
* to do a much better signaling here ;-)
|
||||
*/
|
||||
ppp_connect(pcb, 30);
|
||||
/* OR ppp_listen(pcb); */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Creating a new PPPoS session
|
||||
* ============================
|
||||
*
|
||||
* In lwIP, PPPoS is not PPPoSONET, in lwIP PPPoS is PPPoSerial.
|
||||
*/
|
||||
|
||||
#include "netif/ppp/pppos.h"
|
||||
|
||||
/*
|
||||
* PPPoS serial output callback
|
||||
*
|
||||
* ppp_pcb, PPP control block
|
||||
* data, buffer to write to serial port
|
||||
* len, length of the data buffer
|
||||
* ctx, optional user-provided callback context pointer
|
||||
*
|
||||
* Return value: len if write succeed
|
||||
*/
|
||||
static u32_t output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) {
|
||||
return uart_write(UART, data, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new PPPoS interface
|
||||
*
|
||||
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
||||
* output_cb, PPPoS serial output callback
|
||||
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
||||
* ctx_cb, optional user-provided callback context pointer
|
||||
*/
|
||||
ppp = pppos_create(&ppp_netif,
|
||||
output_cb, status_cb, ctx_cb);
|
||||
|
||||
|
||||
/*
|
||||
* Creating a new PPPoE session
|
||||
* ============================
|
||||
*/
|
||||
|
||||
#include "netif/ppp/pppoe.h"
|
||||
|
||||
/*
|
||||
* Create a new PPPoE interface
|
||||
*
|
||||
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
||||
* ethif, already existing and setup Ethernet interface to use
|
||||
* service_name, PPPoE service name discriminator (not supported yet)
|
||||
* concentrator_name, PPPoE concentrator name discriminator (not supported yet)
|
||||
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
||||
* ctx_cb, optional user-provided callback context pointer
|
||||
*/
|
||||
ppp = pppoe_create(&ppp_netif,
|
||||
ðif,
|
||||
service_name, concentrator_name,
|
||||
status_cb, ctx_cb);
|
||||
|
||||
|
||||
/*
|
||||
* Creating a new PPPoL2TP session
|
||||
* ===============================
|
||||
*/
|
||||
|
||||
#include "netif/ppp/pppol2tp.h"
|
||||
|
||||
/*
|
||||
* Create a new PPPoL2TP interface
|
||||
*
|
||||
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
||||
* netif, optional already existing and setup output netif, necessary if you
|
||||
* want to set this interface as default route to settle the chicken
|
||||
* and egg problem with VPN links
|
||||
* ipaddr, IP to connect to
|
||||
* port, UDP port to connect to (usually 1701)
|
||||
* secret, L2TP secret to use
|
||||
* secret_len, size in bytes of the L2TP secret
|
||||
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
||||
* ctx_cb, optional user-provided callback context pointer
|
||||
*/
|
||||
ppp = pppol2tp_create(&ppp_netif,
|
||||
struct netif *netif, ip_addr_t *ipaddr, u16_t port,
|
||||
u8_t *secret, u8_t secret_len,
|
||||
ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
|
||||
|
||||
|
||||
/*
|
||||
* Initiate PPP client connection
|
||||
* ==============================
|
||||
*/
|
||||
|
||||
/* Set this interface as default route */
|
||||
ppp_set_default(ppp);
|
||||
|
||||
/*
|
||||
* Basic PPP client configuration. Can only be set if PPP session is in the
|
||||
* dead state (i.e. disconnected). We don't need to provide thread-safe
|
||||
* equivalents through PPPAPI because those helpers are only changing
|
||||
* structure members while session is inactive for lwIP core. Configuration
|
||||
* only need to be done once.
|
||||
*/
|
||||
|
||||
/* Ask the peer for up to 2 DNS server addresses. */
|
||||
ppp_set_usepeerdns(ppp, 1);
|
||||
|
||||
/* Auth configuration, this is pretty self-explanatory */
|
||||
ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
|
||||
|
||||
/*
|
||||
* Initiate PPP negotiation, without waiting (holdoff=0), can only be called
|
||||
* if PPP session is in the dead state (i.e. disconnected).
|
||||
*/
|
||||
u16_t holdoff = 0;
|
||||
ppp_connect(ppp, holdoff);
|
||||
|
||||
|
||||
/*
|
||||
* Initiate PPP server listener
|
||||
* ============================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Basic PPP server configuration. Can only be set if PPP session is in the
|
||||
* dead state (i.e. disconnected). We don't need to provide thread-safe
|
||||
* equivalents through PPPAPI because those helpers are only changing
|
||||
* structure members while session is inactive for lwIP core. Configuration
|
||||
* only need to be done once.
|
||||
*/
|
||||
ip4_addr_t addr;
|
||||
|
||||
/* Set our address */
|
||||
IP4_ADDR(&addr, 192,168,0,1);
|
||||
ppp_set_ipcp_ouraddr(ppp, &addr);
|
||||
|
||||
/* Set peer(his) address */
|
||||
IP4_ADDR(&addr, 192,168,0,2);
|
||||
ppp_set_ipcp_hisaddr(ppp, &addr);
|
||||
|
||||
/* Set primary DNS server */
|
||||
IP4_ADDR(&addr, 192,168,10,20);
|
||||
ppp_set_ipcp_dnsaddr(ppp, 0, &addr);
|
||||
|
||||
/* Set secondary DNS server */
|
||||
IP4_ADDR(&addr, 192,168,10,21);
|
||||
ppp_set_ipcp_dnsaddr(ppp, 1, &addr);
|
||||
|
||||
/* Auth configuration, this is pretty self-explanatory */
|
||||
ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
|
||||
|
||||
/* Require peer to authenticate */
|
||||
ppp_set_auth_required(ppp, 1);
|
||||
|
||||
/*
|
||||
* Only for PPPoS, the PPP session should be up and waiting for input.
|
||||
*
|
||||
* Note: for PPPoS, ppp_connect() and ppp_listen() are actually the same thing.
|
||||
* The listen call is meant for future support of PPPoE and PPPoL2TP server
|
||||
* mode, where we will need to negotiate the incoming PPPoE session or L2TP
|
||||
* session before initiating PPP itself. We need this call because there is
|
||||
* two passive modes for PPPoS, ppp_set_passive and ppp_set_silent.
|
||||
*/
|
||||
ppp_set_silent(pppos, 1);
|
||||
|
||||
/*
|
||||
* Initiate PPP listener (i.e. wait for an incoming connection), can only
|
||||
* be called if PPP session is in the dead state (i.e. disconnected).
|
||||
*/
|
||||
ppp_listen(ppp);
|
||||
|
||||
|
||||
/*
|
||||
* Closing PPP connection
|
||||
* ======================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initiate the end of the PPP session, without carrier lost signal
|
||||
* (nocarrier=0), meaning a clean shutdown of PPP protocols.
|
||||
* You can call this function at anytime.
|
||||
*/
|
||||
u8_t nocarrier = 0;
|
||||
ppp_close(ppp, nocarrier);
|
||||
/*
|
||||
* Then you must wait your status_cb() to be called, it may takes from a few
|
||||
* seconds to several tens of seconds depending on the current PPP state.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Freeing a PPP connection
|
||||
* ========================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Free the PPP control block, can only be called if PPP session is in the
|
||||
* dead state (i.e. disconnected). You need to call ppp_close() before.
|
||||
*/
|
||||
ppp_free(ppp);
|
||||
|
||||
|
||||
|
||||
3 PPPoS input path (raw API, IRQ safe API, TCPIP API)
|
||||
=====================================================
|
||||
|
||||
Received data on serial port should be sent to lwIP using the pppos_input()
|
||||
function or the pppos_input_tcpip() function.
|
||||
|
||||
If NO_SYS is 1 and if PPP_INPROC_IRQ_SAFE is 0 (the default), pppos_input()
|
||||
is not IRQ safe and then *MUST* only be called inside your main loop.
|
||||
|
||||
Whatever the NO_SYS value, if PPP_INPROC_IRQ_SAFE is 1, pppos_input() is IRQ
|
||||
safe and can be safely called from an interrupt context, using that is going
|
||||
to reduce your need of buffer if pppos_input() is called byte after byte in
|
||||
your rx serial interrupt.
|
||||
|
||||
if NO_SYS is 0, the thread safe way outside an interrupt context is to use
|
||||
the pppos_input_tcpip() function to pass input data to the lwIP core thread
|
||||
using the TCPIP API. This is thread safe in all cases but you should avoid
|
||||
passing data byte after byte because it uses heavy locking (mailbox) and it
|
||||
allocates pbuf, better fill them !
|
||||
|
||||
if NO_SYS is 0 and if PPP_INPROC_IRQ_SAFE is 1, you may also use pppos_input()
|
||||
from an RX thread, however pppos_input() is not thread safe by itself. You can
|
||||
do that *BUT* you should NEVER call pppos_connect(), pppos_listen() and
|
||||
ppp_free() if pppos_input() can still be running, doing this is NOT thread safe
|
||||
at all. Using PPP_INPROC_IRQ_SAFE from an RX thread is discouraged unless you
|
||||
really know what you are doing, your move ;-)
|
||||
|
||||
|
||||
/*
|
||||
* Fonction to call for received data
|
||||
*
|
||||
* ppp, PPP control block
|
||||
* buffer, input buffer
|
||||
* buffer_len, buffer length in bytes
|
||||
*/
|
||||
void pppos_input(ppp, buffer, buffer_len);
|
||||
|
||||
or
|
||||
|
||||
void pppos_input_tcpip(ppp, buffer, buffer_len);
|
||||
|
||||
|
||||
|
||||
4 Thread safe PPP API (PPPAPI)
|
||||
==============================
|
||||
|
||||
There is a thread safe API for all corresponding ppp_* functions, you have to
|
||||
enable LWIP_PPP_API in your lwipopts.h file, then see
|
||||
include/netif/ppp/pppapi.h, this is actually pretty obvious.
|
||||
|
||||
|
||||
|
||||
5 Notify phase callback (PPP_NOTIFY_PHASE)
|
||||
==========================================
|
||||
|
||||
Notify phase callback, enabled using the PPP_NOTIFY_PHASE config option, let
|
||||
you configure a callback that is called on each PPP internal state change.
|
||||
This is different from the status callback which only warns you about
|
||||
up(running) and down(dead) events.
|
||||
|
||||
Notify phase callback can be used, for example, to set a LED pattern depending
|
||||
on the current phase of the PPP session. Here is a callback example which
|
||||
tries to mimic what we usually see on xDSL modems while they are negotiating
|
||||
the link, which should be self-explanatory:
|
||||
|
||||
static void ppp_notify_phase_cb(ppp_pcb *pcb, u8_t phase, void *ctx) {
|
||||
switch (phase) {
|
||||
|
||||
/* Session is down (either permanently or briefly) */
|
||||
case PPP_PHASE_DEAD:
|
||||
led_set(PPP_LED, LED_OFF);
|
||||
break;
|
||||
|
||||
/* We are between two sessions */
|
||||
case PPP_PHASE_HOLDOFF:
|
||||
led_set(PPP_LED, LED_SLOW_BLINK);
|
||||
break;
|
||||
|
||||
/* Session just started */
|
||||
case PPP_PHASE_INITIALIZE:
|
||||
led_set(PPP_LED, LED_FAST_BLINK);
|
||||
break;
|
||||
|
||||
/* Session is running */
|
||||
case PPP_PHASE_RUNNING:
|
||||
led_set(PPP_LED, LED_ON);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
6 Upgrading from lwIP <= 1.4.x to lwIP >= 2.0.x
|
||||
===============================================
|
||||
|
||||
PPP API was fully reworked between 1.4.x and 2.0.x releases. However porting
|
||||
from previous lwIP version is pretty easy:
|
||||
|
||||
* Previous PPP API used an integer to identify PPP sessions, we are now
|
||||
using ppp_pcb* control block, therefore all functions changed from "int ppp"
|
||||
to "ppp_pcb *ppp"
|
||||
|
||||
* struct netif was moved outside the PPP structure, you have to provide a netif
|
||||
for PPP interface in pppoX_create() functions
|
||||
|
||||
* PPP session are not started automatically after you created them anymore,
|
||||
you have to call ppp_connect(), this way you can configure the session before
|
||||
starting it.
|
||||
|
||||
* Previous PPP API used CamelCase, we are now using snake_case.
|
||||
|
||||
* Previous PPP API mixed PPPoS and PPPoE calls, this isn't the case anymore,
|
||||
PPPoS functions are now prefixed pppos_ and PPPoE functions are now prefixed
|
||||
pppoe_, common functions are now prefixed ppp_.
|
||||
|
||||
* New PPPERR_ error codes added, check you have all of them in your status
|
||||
callback function
|
||||
|
||||
* Only the following include files should now be used in user application:
|
||||
#include "netif/ppp/pppapi.h"
|
||||
#include "netif/ppp/pppos.h"
|
||||
#include "netif/ppp/pppoe.h"
|
||||
#include "netif/ppp/pppol2tp.h"
|
||||
|
||||
Functions from ppp.h can be used, but you don't need to include this header
|
||||
file as it is already included by above header files.
|
||||
|
||||
* PPP_INPROC_OWNTHREAD was broken by design and was removed, you have to create
|
||||
your own serial rx thread
|
||||
|
||||
* PPP_INPROC_MULTITHREADED option was misnamed and confusing and was renamed
|
||||
PPP_INPROC_IRQ_SAFE, please read the "PPPoS input path" documentation above
|
||||
because you might have been fooled by that
|
||||
|
||||
* If you used tcpip_callback_with_block() on ppp_ functions you may wish to use
|
||||
the PPPAPI API instead.
|
||||
|
||||
* ppp_sighup and ppp_close functions were merged using an optional argument
|
||||
"nocarrier" on ppp_close.
|
||||
|
||||
* DNS servers are now only remotely asked if LWIP_DNS is set and if
|
||||
ppp_set_usepeerdns() is set to true, they are now automatically registered
|
||||
using the dns_setserver() function so you don't need to do that in the PPP
|
||||
callback anymore.
|
||||
|
||||
* PPPoS does not use the SIO API anymore, as such it now requires a serial
|
||||
output callback in place of sio_write
|
||||
|
||||
* PPP_MAXIDLEFLAG is now in ms instead of jiffies
|
||||
@@ -1,499 +0,0 @@
|
||||
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 raw API (sometimes called native API) is an event-driven API designed
|
||||
to be used without an operating system that implements zero-copy send and
|
||||
receive. This API is also used by the core stack for interaction between
|
||||
the various protocols. It is the only API available when running lwIP
|
||||
without an operating system.
|
||||
|
||||
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.
|
||||
|
||||
** Multithreading
|
||||
|
||||
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"). When running in a multithreaded
|
||||
environment, raw API functions MUST only be called from the core thread
|
||||
since raw API functions are not protected from concurrent access (aside
|
||||
from pbuf- and memory management functions). 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
|
||||
- pppapi.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.
|
||||
|
||||
Netconn or Socket API functions are thread safe against the
|
||||
core thread but they are not reentrant at the control block
|
||||
granularity level. That is, a UDP or TCP control block must
|
||||
not be shared among multiple threads without proper locking.
|
||||
|
||||
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.
|
||||
|
||||
All 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.
|
||||
|
||||
Do not confuse the lwIP raw API with raw Ethernet or IP sockets.
|
||||
The former is a way of interfacing the lwIP network stack (including
|
||||
TCP and UDP), the later refers to processing raw Ethernet or IP data
|
||||
instead of TCP connections or UDP packets.
|
||||
|
||||
Raw API applications may never block since all packet processing
|
||||
(input and output) as well as timer processing (TCP mainly) is done
|
||||
in a single execution context.
|
||||
|
||||
--- Callbacks
|
||||
|
||||
Program execution is driven by callbacks functions, which are then
|
||||
invoked by the lwIP core when activity related to that application
|
||||
occurs. A particular application may register to be notified via a
|
||||
callback function for events such as incoming data available, outgoing
|
||||
data sent, error notifications, poll timer expiration, connection
|
||||
closed, etc. An application can provide a callback function to perform
|
||||
processing for any or all of these events. 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, ip_addr_t *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_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, ip_addr_t *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, const void *dataptr, u16_t len,
|
||||
u8_t apiflags)
|
||||
|
||||
Enqueues the data pointed to by the argument dataptr. The length of
|
||||
the data is passed as the len parameter. The apiflags can be one or more of:
|
||||
- TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
|
||||
for the data to be copied into. If this flag is not given, no new memory
|
||||
should be allocated and the data should only be referenced by pointer. This
|
||||
also means that the memory behind dataptr must not change until the data is
|
||||
ACKed by the remote host
|
||||
- TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is omitted,
|
||||
the PSH flag is set in the last segment created by this call to tcp_write.
|
||||
If this flag is given, the PSH flag is not set.
|
||||
|
||||
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,
|
||||
err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
|
||||
u8_t interval)
|
||||
|
||||
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.
|
||||
|
||||
ATTENTION: When calling this from one of the TCP callbacks, make
|
||||
sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
|
||||
or you will risk accessing deallocated memory or memory leaks!
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
--- 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, ip_addr_t *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, ip_addr_t *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,
|
||||
ip_addr_t *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 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:
|
||||
|
||||
- lwip_init()
|
||||
|
||||
Initialize the lwIP stack and all of its subsystems.
|
||||
|
||||
- netif_add(struct netif *netif, const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask, const ip4_addr_t *gw,
|
||||
void *state, netif_init_fn init, netif_input_fn input)
|
||||
|
||||
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 its 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_link_up(struct netif *netif)
|
||||
|
||||
This is the hardware link state; e.g. whether cable is plugged for wired
|
||||
Ethernet interface. This function must be called even if you don't know
|
||||
the current state. Having link up and link down events is optional but
|
||||
DHCP and IPv6 discover benefit well from those events.
|
||||
|
||||
- netif_set_up(struct netif *netif)
|
||||
|
||||
This is the administrative (= software) state of the 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.
|
||||
|
||||
You can peek in the netif->dhcp struct for the actual DHCP status.
|
||||
|
||||
- sys_check_timeouts()
|
||||
|
||||
When the system is running, you have to periodically call
|
||||
sys_check_timeouts() which will handle all timers for all protocols in
|
||||
the stack; add this to your main loop or equivalent.
|
||||
|
||||
|
||||
--- 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 <your_checksum_routine>.
|
||||
|
||||
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_htons(x) <your_htons>
|
||||
#define lwip_htonl(x) <your_htonl>
|
||||
If you #define them to htons() and htonl(), you should
|
||||
#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
|
||||
defining hton*/ntoh* compatibility macros.
|
||||
|
||||
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.
|
||||
|
||||
For more optimization hints take a look at the lwIP wiki.
|
||||
|
||||
--- Zero-copy MACs
|
||||
|
||||
To achieve zero-copy on transmit, the data passed to the raw API must
|
||||
remain unchanged until sent. Because the send- (or write-)functions return
|
||||
when the packets have been enqueued for sending, data must be kept stable
|
||||
after that, too.
|
||||
|
||||
This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions
|
||||
must *not* be reused by the application unless their ref-count is 1.
|
||||
|
||||
For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too,
|
||||
but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while
|
||||
PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change).
|
||||
|
||||
Also, data passed to tcp_write without the copy-flag must not be changed!
|
||||
|
||||
Therefore, be careful which type of PBUF you use and if you copy TCP data
|
||||
or not!
|
||||
@@ -1,120 +0,0 @@
|
||||
Daily Use Guide for using Savannah for lwIP
|
||||
|
||||
Table of Contents:
|
||||
|
||||
1 - Obtaining lwIP from the Git repository
|
||||
2 - Committers/developers Git access using SSH
|
||||
3 - Merging a development branch to master branch
|
||||
4 - How to release lwIP
|
||||
|
||||
|
||||
|
||||
1 Obtaining lwIP from the Git repository
|
||||
----------------------------------------
|
||||
|
||||
To perform an anonymous Git clone of the master branch (this is where
|
||||
bug fixes and incremental enhancements occur), do this:
|
||||
git clone git://git.savannah.nongnu.org/lwip.git
|
||||
|
||||
Or, obtain a stable branch (updated with bug fixes only) as follows:
|
||||
git clone --branch DEVEL-1_4_1 git://git.savannah.nongnu.org/lwip.git
|
||||
|
||||
Or, obtain a specific (fixed) release as follows:
|
||||
git clone --branch STABLE-1_4_1 git://git.savannah.nongnu.org/lwip.git
|
||||
|
||||
|
||||
2 Committers/developers Git access using SSH
|
||||
--------------------------------------------
|
||||
|
||||
The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
|
||||
As such, Git 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@git.sv.gnu.org
|
||||
|
||||
If it tells you:
|
||||
Linux vcs.savannah.gnu.org 2.6.32-5-xen-686 #1 SMP Wed Jun 17 17:10:03 UTC 2015 i686
|
||||
|
||||
Interactive shell login is not possible for security reasons.
|
||||
VCS commands are allowed.
|
||||
Last login: Tue May 15 23:10:12 2012 from 82.245.102.129
|
||||
You tried to execute:
|
||||
Sorry, you are not allowed to execute that command.
|
||||
Shared connection to git.sv.gnu.org closed.
|
||||
|
||||
then you could login; Savannah refuses to give you a shell - which is OK, as we
|
||||
are allowed to use SSH for Git only. Now, you should be able to do this:
|
||||
git clone your_login@git.sv.gnu.org:/srv/git/lwip.git
|
||||
|
||||
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 Git 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 https://savannah.nongnu.org/git/?group=lwip
|
||||
|
||||
|
||||
3 - Merging a development branch to master branch
|
||||
-------------------------------------------------
|
||||
|
||||
Merging is a straightforward process in Git. How to merge all changes in a
|
||||
development branch since our last merge from main:
|
||||
|
||||
Checkout the master branch:
|
||||
git checkout master
|
||||
|
||||
Merge the development branch to master:
|
||||
git merge your-development-branch
|
||||
|
||||
Resolve any conflict.
|
||||
|
||||
Commit the merge result.
|
||||
git commit -a
|
||||
|
||||
Push your commits:
|
||||
git push
|
||||
|
||||
|
||||
4 How to release lwIP
|
||||
---------------------
|
||||
|
||||
First, tag the release using Git: (I use release number 1.4.1 throughout
|
||||
this example).
|
||||
git tag -a STABLE-1_4_1
|
||||
|
||||
Share the tag reference by pushing it to remote:
|
||||
git push origin STABLE-1_4_1
|
||||
|
||||
Prepare the release:
|
||||
cp -r lwip lwip-1.4.1
|
||||
rm -rf lwip-1.4.1/.git lwip-1.4.1/.gitattributes
|
||||
|
||||
Archive the current directory using tar, gzip'd, bzip2'd and zip'd.
|
||||
tar czvf lwip-1.4.1.tar.gz lwip-1.4.1
|
||||
tar cjvf lwip-1.4.1.tar.bz2 lwip-1.4.1
|
||||
zip -r lwip-1.4.1.zip lwip-1.4.1
|
||||
|
||||
Now, sign the archives with a detached GPG binary signature as follows:
|
||||
gpg -b lwip-1.4.1.tar.gz
|
||||
gpg -b lwip-1.4.1.tar.bz2
|
||||
gpg -b lwip-1.4.1.zip
|
||||
|
||||
Upload these files using anonymous FTP:
|
||||
ncftp ftp://savannah.gnu.org/incoming/savannah/lwip
|
||||
ncftp> mput *1.4.1.*
|
||||
|
||||
Additionally, you may post a news item on Savannah, like this:
|
||||
|
||||
A new 1.4.1 release is now available here:
|
||||
http://savannah.nongnu.org/files/?group=lwip&highlight=1.4.1
|
||||
|
||||
You will have to submit this via the user News interface, then approve
|
||||
this via the Administrator News interface.
|
||||
@@ -1,303 +0,0 @@
|
||||
sys_arch interface for lwIP
|
||||
|
||||
Author: Adam Dunkels
|
||||
Simon Goldschmidt
|
||||
|
||||
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, mailboxes and mutexes 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. Timer scheduling is implemented in lwIP, but can be implemented
|
||||
by the sys_arch port (LWIP_TIMERS_CUSTOM==1).
|
||||
|
||||
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 should be implemented as a queue which allows multiple messages
|
||||
to be posted (implementing as a rendez-vous point where only one message can be
|
||||
posted at a time can have a highly negative impact on performance). 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". Mutexes are represented by the type "sys_mutex_t".
|
||||
lwIP does not place any restrictions on how these types are represented
|
||||
internally.
|
||||
|
||||
Since lwIP 1.4.0, semaphore, mutexes and mailbox functions are prototyped in a way that
|
||||
allows both using pointers or actual OS structures to be used. This way, memory
|
||||
required for such types can be either allocated in place (globally or on the
|
||||
stack) or on the heap (allocated internally in the "*_new()" functions).
|
||||
|
||||
The following functions must be implemented by the sys_arch:
|
||||
|
||||
- void sys_init(void)
|
||||
|
||||
Is called to initialize the sys_arch layer.
|
||||
|
||||
- err_t sys_sem_new(sys_sem_t *sem, u8_t count)
|
||||
|
||||
Creates a new semaphore. The semaphore is allocated to the memory that 'sem'
|
||||
points to (which can be both a pointer or the actual OS structure).
|
||||
The "count" argument specifies the initial state of the semaphore (which is
|
||||
either 0 or 1).
|
||||
If the semaphore has been created, ERR_OK should be returned. Returning any
|
||||
other error will provide a hint what went wrong, but except for assertions,
|
||||
no real error handling is implemented.
|
||||
|
||||
- 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.
|
||||
|
||||
- int sys_sem_valid(sys_sem_t *sem)
|
||||
|
||||
Returns 1 if the semaphore is valid, 0 if it is not valid.
|
||||
When using pointers, a simple way is to check the pointer for != NULL.
|
||||
When directly using OS structures, implementing this may be more complex.
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_sem_set_invalid(sys_sem_t *sem)
|
||||
|
||||
Invalidate a semaphore so that sys_sem_valid() returns 0.
|
||||
ATTENTION: This does NOT mean that the semaphore shall be deallocated:
|
||||
sys_sem_free() is always called before calling this function!
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_mutex_new(sys_mutex_t *mutex)
|
||||
|
||||
Creates a new mutex. The mutex is allocated to the memory that 'mutex'
|
||||
points to (which can be both a pointer or the actual OS structure).
|
||||
If the mutex has been created, ERR_OK should be returned. Returning any
|
||||
other error will provide a hint what went wrong, but except for assertions,
|
||||
no real error handling is implemented.
|
||||
|
||||
- void sys_mutex_free(sys_mutex_t *mutex)
|
||||
|
||||
Deallocates a mutex.
|
||||
|
||||
- void sys_mutex_lock(sys_mutex_t *mutex)
|
||||
|
||||
Blocks the thread until the mutex can be grabbed.
|
||||
|
||||
- void sys_mutex_unlock(sys_mutex_t *mutex)
|
||||
|
||||
Releases the mutex previously locked through 'sys_mutex_lock()'.
|
||||
|
||||
- void sys_mutex_valid(sys_mutex_t *mutex)
|
||||
|
||||
Returns 1 if the mutes is valid, 0 if it is not valid.
|
||||
When using pointers, a simple way is to check the pointer for != NULL.
|
||||
When directly using OS structures, implementing this may be more complex.
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_mutex_set_invalid(sys_mutex_t *mutex)
|
||||
|
||||
Invalidate a mutex so that sys_mutex_valid() returns 0.
|
||||
ATTENTION: This does NOT mean that the mutex shall be deallocated:
|
||||
sys_mutex_free() is always called before calling this function!
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- err_t sys_mbox_new(sys_mbox_t *mbox, 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.
|
||||
If the mailbox has been created, ERR_OK should be returned. Returning any
|
||||
other error will provide a hint what went wrong, but except for assertions,
|
||||
no real error handling is implemented.
|
||||
|
||||
- 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.
|
||||
|
||||
- int sys_mbox_valid(sys_mbox_t *mbox)
|
||||
|
||||
Returns 1 if the mailbox is valid, 0 if it is not valid.
|
||||
When using pointers, a simple way is to check the pointer for != NULL.
|
||||
When directly using OS structures, implementing this may be more complex.
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_mbox_set_invalid(sys_mbox_t *mbox)
|
||||
|
||||
Invalidate a mailbox so that sys_mbox_valid() returns 0.
|
||||
ATTENTION: This does NOT mean that the mailbox shall be deallocated:
|
||||
sys_mbox_free() is always called before calling this function!
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
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.
|
||||
|
||||
When lwIP is used from more than one context (e.g. from multiple threads OR from
|
||||
main-loop and from interrupts), the SYS_LIGHTWEIGHT_PROT protection SHOULD be enabled!
|
||||
|
||||
- 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.
|
||||
|
||||
For some configurations, you also need:
|
||||
|
||||
- u32_t sys_now(void)
|
||||
|
||||
This optional function returns the current time in milliseconds (don't care
|
||||
for wraparound, this is only used for time diffs).
|
||||
Not implementing this function means you cannot use some modules (e.g. TCP
|
||||
timestamps, internal timeouts for NO_SYS==1).
|
||||
|
||||
|
||||
Note:
|
||||
|
||||
Be careful 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 <errno.h> 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
|
||||
@@ -78,8 +78,8 @@ APIFILES=$(LWIPDIR)/api/api_lib.c \
|
||||
$(LWIPDIR)/api/tcpip.c
|
||||
|
||||
# NETIFFILES: Files implementing various generic network interface functions
|
||||
NETIFFILES=$(LWIPDIR)/netif/ethernet.c \
|
||||
$(LWIPDIR)/netif/slipif.c
|
||||
NETIFFILES=$(LWIPDIR)/netif/ethernet.c
|
||||
#\ $(LWIPDIR)/netif/slipif.c
|
||||
|
||||
# SIXLOWPAN: 6LoWPAN
|
||||
SIXLOWPAN=$(LWIPDIR)/netif/lowpan6.c \
|
||||
@@ -122,8 +122,8 @@ LWIPNOAPPSFILES=$(COREFILES) \
|
||||
$(CORE6FILES) \
|
||||
$(APIFILES) \
|
||||
$(NETIFFILES) \
|
||||
$(PPPFILES) \
|
||||
$(SIXLOWPAN)
|
||||
# $(PPPFILES)
|
||||
|
||||
# SNMPFILES: SNMPv2c agent
|
||||
SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include <errno.h>
|
||||
//#include "lwip/errno.h"
|
||||
#include "lwip/errno.h"
|
||||
|
||||
#if !NO_SYS
|
||||
/** Table to quickly map an lwIP error (err_t) to a socket error
|
||||
|
||||
@@ -1001,7 +1001,7 @@ tcp_slowtmr_start:
|
||||
prev = NULL;
|
||||
pcb = tcp_active_pcbs;
|
||||
if (pcb == NULL) {
|
||||
LWIP_DEBUGF(TCP_DEBUG_TMR, ("tcp_slowtmr: no active pcbs\n"));
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
|
||||
}
|
||||
while (pcb != NULL) {
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
|
||||
|
||||
@@ -29,65 +29,42 @@
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
#ifndef __ARCH_CC_H__
|
||||
#define __ARCH_CC_H__
|
||||
#ifndef LWIP_ARCH_CC_H
|
||||
#define LWIP_ARCH_CC_H
|
||||
|
||||
/* Include some files for defining library routines */
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define LWIP_TIMEVAL_PRIVATE 0
|
||||
|
||||
/* Define platform endianness */
|
||||
#ifndef BYTE_ORDER
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#endif /* BYTE_ORDER */
|
||||
|
||||
/* Define generic types used in lwIP */
|
||||
typedef unsigned char u8_t;
|
||||
typedef signed char s8_t;
|
||||
typedef unsigned short u16_t;
|
||||
typedef signed short s16_t;
|
||||
typedef unsigned int u32_t;
|
||||
typedef signed int s32_t;
|
||||
|
||||
typedef unsigned long mem_ptr_t;
|
||||
|
||||
/* Define (sn)printf formatters for these lwIP types */
|
||||
#define X8_F "02x"
|
||||
#define U16_F "hu"
|
||||
#define S16_F "hd"
|
||||
#define X16_F "hx"
|
||||
#define U32_F "u"
|
||||
#define S32_F "d"
|
||||
#define X32_F "x"
|
||||
|
||||
/* If only we could use C99 and get %zu */
|
||||
#if defined(__x86_64__)
|
||||
#define SZT_F "lu"
|
||||
#else
|
||||
#define SZT_F "u"
|
||||
/* see https://sourceforge.net/p/predef/wiki/OperatingSystems/ */
|
||||
#if defined __ANDROID__
|
||||
#define LWIP_UNIX_ANDROID
|
||||
#elif defined __linux__
|
||||
#define LWIP_UNIX_LINUX
|
||||
#elif defined __APPLE__
|
||||
#define LWIP_UNIX_MACH
|
||||
#elif defined __OpenBSD__
|
||||
#define LWIP_UNIX_OPENBSD
|
||||
#elif defined __CYGWIN__
|
||||
#define LWIP_UNIX_CYGWIN
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
#define LWIP_TIMEVAL_PRIVATE 0
|
||||
#include <sys/time.h>
|
||||
|
||||
/* prototypes for printf() and abort() */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/* Plaform specific diagnostic output */
|
||||
//#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
|
||||
#include "Debug.hpp"
|
||||
#define LWIP_PLATFORM_DIAG(x) DEBUG_STACK x;
|
||||
|
||||
#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
|
||||
x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
|
||||
#define LWIP_ERRNO_INCLUDE <errno.h>
|
||||
|
||||
#define LWIP_RAND() ((u32_t)rand())
|
||||
|
||||
#endif /* __ARCH_CC_H__ */
|
||||
/* different handling for unit test, normally not needed */
|
||||
#ifdef LWIP_NOASSERT_ON_ERROR
|
||||
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
|
||||
handler;}} while(0)
|
||||
#endif
|
||||
|
||||
#if defined(LWIP_UNIX_ANDROID) && defined(FD_SET)
|
||||
typedef __kernel_fd_set fd_set;
|
||||
#endif
|
||||
|
||||
struct sio_status_s;
|
||||
typedef struct sio_status_s sio_status_t;
|
||||
#define sio_fd_t sio_status_t*
|
||||
#define __sio_fd_t_defined
|
||||
|
||||
#endif /* LWIP_ARCH_CC_H */
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
#ifndef __ARCH_PERF_H__
|
||||
#define __ARCH_PERF_H__
|
||||
#ifndef LWIP_ARCH_PERF_H
|
||||
#define LWIP_ARCH_PERF_H
|
||||
|
||||
#include <sys/times.h>
|
||||
|
||||
@@ -60,4 +60,4 @@ void perf_print_times(struct tms *start, struct tms *end, char *key);
|
||||
|
||||
void perf_init(char *fname);
|
||||
|
||||
#endif /* __ARCH_PERF_H__ */
|
||||
#endif /* LWIP_ARCH_PERF_H */
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
#ifndef __ARCH_SYS_ARCH_H__
|
||||
#define __ARCH_SYS_ARCH_H__
|
||||
|
||||
#include <errno.h>
|
||||
#ifndef LWIP_ARCH_SYS_ARCH_H
|
||||
#define LWIP_ARCH_SYS_ARCH_H
|
||||
|
||||
#define SYS_MBOX_NULL NULL
|
||||
#define SYS_SEM_NULL NULL
|
||||
@@ -41,19 +39,25 @@ typedef u32_t sys_prot_t;
|
||||
|
||||
struct sys_sem;
|
||||
typedef struct sys_sem * sys_sem_t;
|
||||
#define sys_sem_valid(sem) (((sem) != NULL) && (*(sem) != NULL))
|
||||
#define sys_sem_set_invalid(sem) do { if((sem) != NULL) { *(sem) = NULL; }}while(0)
|
||||
#define sys_sem_valid(sem) (((sem) != NULL) && (*(sem) != NULL))
|
||||
#define sys_sem_valid_val(sem) ((sem) != NULL)
|
||||
#define sys_sem_set_invalid(sem) do { if((sem) != NULL) { *(sem) = NULL; }}while(0)
|
||||
#define sys_sem_set_invalid_val(sem) do { (sem) = NULL; }while(0)
|
||||
|
||||
/* let sys.h use binary semaphores for mutexes */
|
||||
#define LWIP_COMPAT_MUTEX 1
|
||||
struct sys_mutex;
|
||||
typedef struct sys_mutex * sys_mutex_t;
|
||||
#define sys_mutex_valid(mutex) sys_sem_valid(mutex)
|
||||
#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex)
|
||||
|
||||
struct sys_mbox;
|
||||
typedef struct sys_mbox *sys_mbox_t;
|
||||
#define sys_mbox_valid(mbox) (((mbox) != NULL) && (*(mbox) != NULL))
|
||||
#define sys_mbox_set_invalid(mbox) do { if((mbox) != NULL) { *(mbox) = NULL; }}while(0)
|
||||
typedef struct sys_mbox * sys_mbox_t;
|
||||
#define sys_mbox_valid(mbox) sys_sem_valid(mbox)
|
||||
#define sys_mbox_valid_val(mbox) sys_sem_valid_val(mbox)
|
||||
#define sys_mbox_set_invalid(mbox) sys_sem_set_invalid(mbox)
|
||||
#define sys_mbox_set_invalid_val(mbox) sys_sem_set_invalid_val(mbox)
|
||||
|
||||
struct sys_thread;
|
||||
typedef struct sys_thread * sys_thread_t;
|
||||
|
||||
#endif /* __ARCH_SYS_ARCH_H__ */
|
||||
#endif /* LWIP_ARCH_SYS_ARCH_H */
|
||||
|
||||
|
||||
@@ -75,8 +75,9 @@
|
||||
* in turn pull in a lot of standard libary code. In resource-constrained
|
||||
* systems, this should be defined to something less resource-consuming.
|
||||
*/
|
||||
#include "Debug.hpp"
|
||||
#ifndef LWIP_PLATFORM_DIAG
|
||||
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
|
||||
#define LWIP_PLATFORM_DIAG(x) DEBUG_STACK x
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
@@ -136,30 +137,16 @@ typedef uintptr_t mem_ptr_t;
|
||||
/* Define (sn)printf formatters for these lwIP types */
|
||||
#if !LWIP_NO_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#ifndef X8_F
|
||||
#define X8_F "02" PRIx8
|
||||
#endif
|
||||
#ifndef U16_F
|
||||
#define U16_F PRIu16
|
||||
#endif
|
||||
#ifndef S16_F
|
||||
#define S16_F PRId16
|
||||
#endif
|
||||
#ifndef X16_F
|
||||
#define X16_F PRIx16
|
||||
#endif
|
||||
#ifndef U32_F
|
||||
#define U32_F PRIu32
|
||||
#endif
|
||||
#ifndef S32_F
|
||||
#define S32_F PRId32
|
||||
#endif
|
||||
#ifndef X32_F
|
||||
#define X32_F PRIx32
|
||||
#endif
|
||||
#ifndef SZT_F
|
||||
#define SZT_F PRIuPTR
|
||||
#endif
|
||||
#define U8_F "c"
|
||||
#define S8_F "c"
|
||||
#define X8_F "x"
|
||||
#define U16_F "u"
|
||||
#define S16_F "d"
|
||||
#define X16_F "x"
|
||||
#define U32_F "u"
|
||||
#define S32_F "d"
|
||||
#define X32_F "x"
|
||||
#define SZT_F "d"
|
||||
#endif
|
||||
|
||||
/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
|
||||
|
||||
@@ -181,7 +181,7 @@ extern int errno;
|
||||
|
||||
/* Define LWIP_ERRNO_INCLUDE to <errno.h> to include the error defines here */
|
||||
#ifdef LWIP_ERRNO_INCLUDE
|
||||
#include LWIP_ERRNO_INCLUDE
|
||||
//#include
|
||||
#endif /* LWIP_ERRNO_INCLUDE */
|
||||
|
||||
#endif /* LWIP_PROVIDE_ERRNO */
|
||||
@@ -40,13 +40,15 @@
|
||||
#define LWIP_HDR_SOCKETS_H
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/errno.h"
|
||||
//#include "lwip/errno.h"
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -508,42 +510,6 @@ int lwip_fcntl(int s, int cmd, int val);
|
||||
|
||||
#if LWIP_COMPAT_SOCKETS
|
||||
#if LWIP_COMPAT_SOCKETS != 2
|
||||
/** @ingroup socket */
|
||||
#define accept(s,addr,addrlen) lwip_accept(s,addr,addrlen)
|
||||
/** @ingroup socket */
|
||||
#define bind(s,name,namelen) lwip_bind(s,name,namelen)
|
||||
/** @ingroup socket */
|
||||
#define shutdown(s,how) lwip_shutdown(s,how)
|
||||
/** @ingroup socket */
|
||||
#define getpeername(s,name,namelen) lwip_getpeername(s,name,namelen)
|
||||
/** @ingroup socket */
|
||||
#define getsockname(s,name,namelen) lwip_getsockname(s,name,namelen)
|
||||
/** @ingroup socket */
|
||||
#define setsockopt(s,level,optname,opval,optlen) lwip_setsockopt(s,level,optname,opval,optlen)
|
||||
/** @ingroup socket */
|
||||
#define getsockopt(s,level,optname,opval,optlen) lwip_getsockopt(s,level,optname,opval,optlen)
|
||||
/** @ingroup socket */
|
||||
#define closesocket(s) lwip_close(s)
|
||||
/** @ingroup socket */
|
||||
#define connect(s,name,namelen) lwip_connect(s,name,namelen)
|
||||
/** @ingroup socket */
|
||||
#define listen(s,backlog) lwip_listen(s,backlog)
|
||||
/** @ingroup socket */
|
||||
#define recv(s,mem,len,flags) lwip_recv(s,mem,len,flags)
|
||||
/** @ingroup socket */
|
||||
#define recvfrom(s,mem,len,flags,from,fromlen) lwip_recvfrom(s,mem,len,flags,from,fromlen)
|
||||
/** @ingroup socket */
|
||||
#define send(s,dataptr,size,flags) lwip_send(s,dataptr,size,flags)
|
||||
/** @ingroup socket */
|
||||
#define sendmsg(s,message,flags) lwip_sendmsg(s,message,flags)
|
||||
/** @ingroup socket */
|
||||
#define sendto(s,dataptr,size,flags,to,tolen) lwip_sendto(s,dataptr,size,flags,to,tolen)
|
||||
/** @ingroup socket */
|
||||
#define socket(domain,type,protocol) lwip_socket(domain,type,protocol)
|
||||
/** @ingroup socket */
|
||||
#define select(maxfdp1,readset,writeset,exceptset,timeout) lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
|
||||
/** @ingroup socket */
|
||||
#define ioctlsocket(s,cmd,argp) lwip_ioctl(s,cmd,argp)
|
||||
|
||||
#if LWIP_POSIX_SOCKETS_IO_NAMES
|
||||
/** @ingroup socket */
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if NO_SYS
|
||||
|
||||
/* For a totally minimal and standalone system, we provide null
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2001, 2002 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 <adam@sics.se>
|
||||
#
|
||||
|
||||
all compile: lwip_fuzz
|
||||
.PHONY: all clean
|
||||
|
||||
CC=afl-gcc
|
||||
LDFLAGS=-lm
|
||||
CFLAGS=-O0
|
||||
|
||||
CONTRIBDIR=../../../lwip-contrib
|
||||
include $(CONTRIBDIR)/ports/unix/Common.mk
|
||||
|
||||
clean:
|
||||
rm -f *.o $(LWIPLIBCOMMON) lwip_fuzz *.s .depend* *.core core
|
||||
|
||||
depend dep: .depend
|
||||
|
||||
include .depend
|
||||
|
||||
.depend: fuzz.c $(LWIPFILES) $(APPFILES)
|
||||
$(CCDEP) $(CFLAGS) -MM $^ > .depend || rm -f .depend
|
||||
|
||||
lwip_fuzz: .depend $(LWIPLIBCOMMON) fuzz.o
|
||||
$(CC) $(CFLAGS) -o lwip_fuzz fuzz.o $(LWIPLIBCOMMON) $(LDFLAGS)
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
Fuzzing the lwIP stack (afl-fuzz requires linux/unix or similar)
|
||||
|
||||
This directory contains a small app that reads Ethernet frames from stdin and
|
||||
processes them. It is used together with the 'american fuzzy lop' tool (found
|
||||
at http://lcamtuf.coredump.cx/afl/) and the sample inputs to test how
|
||||
unexpected inputs are handled. The afl tool will read the known inputs, and
|
||||
try to modify them to exercise as many code paths as possible, by instrumenting
|
||||
the code and keeping track of which code is executed.
|
||||
|
||||
Just running make will produce the test program.
|
||||
|
||||
Then run afl with:
|
||||
|
||||
afl-fuzz -i inputs/<INPUT> -o output ./lwip_fuzz
|
||||
|
||||
and it should start working. It will probably complain about CPU scheduler,
|
||||
set AFL_SKIP_CPUFREQ=1 to ignore it.
|
||||
If it complains about invalid "/proc/sys/kernel/core_pattern" setting, try
|
||||
executing "sudo bash -c 'echo core > /proc/sys/kernel/core_pattern'".
|
||||
|
||||
The input is split into different subdirectories since they test different
|
||||
parts of the code, and since you want to run one instance of afl-fuzz on each
|
||||
core.
|
||||
|
||||
When afl finds a crash or a hang, the input that caused it will be placed in
|
||||
the output directory. If you have hexdump and text2pcap tools installed,
|
||||
running output_to_pcap.sh <outputdir> will create pcap files for each input
|
||||
file to simplify viewing in wireshark.
|
||||
|
||||
The lwipopts.h file needs to have checksum checking off, otherwise almost every
|
||||
packet will be discarded because of that. The other options can be tuned to
|
||||
expose different parts of the code.
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 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: Erik Ekman <erik@kryo.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/init.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "netif/etharp.h"
|
||||
#if LWIP_IPV6
|
||||
#include "lwip/ethip6.h"
|
||||
#include "lwip/nd6.h"
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* no-op send function */
|
||||
static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
LWIP_UNUSED_ARG(netif);
|
||||
LWIP_UNUSED_ARG(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static err_t testif_init(struct netif *netif)
|
||||
{
|
||||
netif->name[0] = 'f';
|
||||
netif->name[1] = 'z';
|
||||
netif->output = etharp_output;
|
||||
netif->linkoutput = lwip_tx_func;
|
||||
netif->mtu = 1500;
|
||||
netif->hwaddr_len = 6;
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
|
||||
|
||||
netif->hwaddr[0] = 0x00;
|
||||
netif->hwaddr[1] = 0x23;
|
||||
netif->hwaddr[2] = 0xC1;
|
||||
netif->hwaddr[3] = 0xDE;
|
||||
netif->hwaddr[4] = 0xD0;
|
||||
netif->hwaddr[5] = 0x0D;
|
||||
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = ethip6_output;
|
||||
netif->ip6_autoconfig_enabled = 1;
|
||||
netif_create_ip6_linklocal_address(netif, 1);
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#endif
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void input_pkt(struct netif *netif, const u8_t *data, size_t len)
|
||||
{
|
||||
struct pbuf *p, *q;
|
||||
err_t err;
|
||||
|
||||
LWIP_ASSERT("pkt too big", len <= 0xFFFF);
|
||||
p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
|
||||
LWIP_ASSERT("alloc failed", p);
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
MEMCPY(q->payload, data, q->len);
|
||||
data += q->len;
|
||||
}
|
||||
err = netif->input(p, netif);
|
||||
if (err != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
struct netif net_test;
|
||||
ip4_addr_t addr;
|
||||
ip4_addr_t netmask;
|
||||
ip4_addr_t gw;
|
||||
u8_t pktbuf[2000];
|
||||
size_t len;
|
||||
|
||||
lwip_init();
|
||||
|
||||
IP4_ADDR(&addr, 172, 30, 115, 84);
|
||||
IP4_ADDR(&netmask, 255, 255, 255, 0);
|
||||
IP4_ADDR(&gw, 172, 30, 115, 1);
|
||||
|
||||
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
|
||||
netif_set_up(&net_test);
|
||||
|
||||
#if LWIP_IPV6
|
||||
nd6_tmr(); /* tick nd to join multicast groups */
|
||||
#endif
|
||||
|
||||
if(argc > 1) {
|
||||
FILE* f;
|
||||
const char* filename;
|
||||
printf("reading input from file... ");
|
||||
fflush(stdout);
|
||||
filename = argv[1];
|
||||
LWIP_ASSERT("invalid filename", filename != NULL);
|
||||
f = fopen(filename, "rb");
|
||||
LWIP_ASSERT("open failed", f != NULL);
|
||||
len = fread(pktbuf, 1, sizeof(pktbuf), f);
|
||||
fclose(f);
|
||||
printf("testing file: \"%s\"...\r\n", filename);
|
||||
} else {
|
||||
len = fread(pktbuf, 1, sizeof(pktbuf), stdin);
|
||||
}
|
||||
input_pkt(&net_test, pktbuf, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 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: Simon Goldschmidt
|
||||
*
|
||||
*/
|
||||
#ifndef LWIP_HDR_LWIPOPTS_H__
|
||||
#define LWIP_HDR_LWIPOPTS_H__
|
||||
|
||||
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
|
||||
#define NO_SYS 1
|
||||
#define LWIP_NETCONN 0
|
||||
#define LWIP_SOCKET 0
|
||||
#define SYS_LIGHTWEIGHT_PROT 0
|
||||
|
||||
#define LWIP_IPV6 1
|
||||
#define IPV6_FRAG_COPYHEADER 1
|
||||
#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 0
|
||||
|
||||
/* Enable DHCP to test it */
|
||||
#define LWIP_DHCP 1
|
||||
|
||||
/* Turn off checksum verification of fuzzed data */
|
||||
#define CHECKSUM_CHECK_IP 0
|
||||
#define CHECKSUM_CHECK_UDP 0
|
||||
#define CHECKSUM_CHECK_TCP 0
|
||||
#define CHECKSUM_CHECK_ICMP 0
|
||||
#define CHECKSUM_CHECK_ICMP6 0
|
||||
|
||||
/* Minimal changes to opt.h required for tcp unit tests: */
|
||||
#define MEM_SIZE 16000
|
||||
#define TCP_SND_QUEUELEN 40
|
||||
#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
|
||||
#define TCP_SND_BUF (12 * TCP_MSS)
|
||||
#define TCP_WND (10 * TCP_MSS)
|
||||
#define LWIP_WND_SCALE 1
|
||||
#define TCP_RCV_SCALE 0
|
||||
#define PBUF_POOL_SIZE 400 /* pbuf tests need ~200KByte */
|
||||
|
||||
/* Minimal changes to opt.h required for etharp unit tests: */
|
||||
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
|
||||
|
||||
#endif /* LWIP_HDR_LWIPOPTS_H__ */
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "This script will make pcap files from the afl-fuzz crash/hang files"
|
||||
echo "It needs hexdump and text2pcap"
|
||||
echo "Please give output directory as argument"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for i in `ls $1/crashes/id*`
|
||||
do
|
||||
PCAPNAME=`echo $i | grep pcap`
|
||||
if [ -z "$PCAPNAME" ]; then
|
||||
hexdump -C $i > $1/$$.tmp
|
||||
text2pcap $1/$$.tmp ${i}.pcap
|
||||
fi
|
||||
done
|
||||
for i in `ls $1/hangs/id*`
|
||||
do
|
||||
PCAPNAME=`echo $i | grep pcap`
|
||||
if [ -z "$PCAPNAME" ]; then
|
||||
hexdump -C $i > $1/$$.tmp
|
||||
text2pcap $1/$$.tmp ${i}.pcap
|
||||
fi
|
||||
done
|
||||
rm -f $1/$$.tmp
|
||||
|
||||
echo
|
||||
echo "Created pcap files:"
|
||||
ls $1/*/*.pcap
|
||||
@@ -1,121 +0,0 @@
|
||||
#include "test_mem.h"
|
||||
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#if !LWIP_STATS || !MEM_STATS
|
||||
#error "This tests needs MEM-statistics enabled"
|
||||
#endif
|
||||
#if LWIP_DNS
|
||||
#error "This test needs DNS turned off (as it mallocs on init)"
|
||||
#endif
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
mem_setup(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
mem_teardown(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Test functions */
|
||||
|
||||
/** Call mem_malloc, mem_free and mem_trim and check stats */
|
||||
START_TEST(test_mem_one)
|
||||
{
|
||||
#define SIZE1 16
|
||||
#define SIZE1_2 12
|
||||
#define SIZE2 16
|
||||
void *p1, *p2;
|
||||
mem_size_t s1, s2;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
|
||||
p1 = mem_malloc(SIZE1);
|
||||
fail_unless(p1 != NULL);
|
||||
fail_unless(lwip_stats.mem.used >= SIZE1);
|
||||
s1 = lwip_stats.mem.used;
|
||||
|
||||
p2 = mem_malloc(SIZE2);
|
||||
fail_unless(p2 != NULL);
|
||||
fail_unless(lwip_stats.mem.used >= SIZE2 + s1);
|
||||
s2 = lwip_stats.mem.used;
|
||||
|
||||
mem_trim(p1, SIZE1_2);
|
||||
|
||||
mem_free(p2);
|
||||
fail_unless(lwip_stats.mem.used <= s2 - SIZE2);
|
||||
|
||||
mem_free(p1);
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void malloc_keep_x(int x, int num, int size, int freestep)
|
||||
{
|
||||
int i;
|
||||
void* p[16];
|
||||
LWIP_ASSERT("invalid size", size >= 0 && size < (mem_size_t)-1);
|
||||
memset(p, 0, sizeof(p));
|
||||
for(i = 0; i < num && i < 16; i++) {
|
||||
p[i] = mem_malloc((mem_size_t)size);
|
||||
fail_unless(p[i] != NULL);
|
||||
}
|
||||
for(i = 0; i < num && i < 16; i += freestep) {
|
||||
if (i == x) {
|
||||
continue;
|
||||
}
|
||||
mem_free(p[i]);
|
||||
p[i] = NULL;
|
||||
}
|
||||
for(i = 0; i < num && i < 16; i++) {
|
||||
if (i == x) {
|
||||
continue;
|
||||
}
|
||||
if (p[i] != NULL) {
|
||||
mem_free(p[i]);
|
||||
p[i] = NULL;
|
||||
}
|
||||
}
|
||||
fail_unless(p[x] != NULL);
|
||||
mem_free(p[x]);
|
||||
}
|
||||
|
||||
START_TEST(test_mem_random)
|
||||
{
|
||||
const int num = 16;
|
||||
int x;
|
||||
int size;
|
||||
int freestep;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
|
||||
for (x = 0; x < num; x++) {
|
||||
for (size = 1; size < 32; size++) {
|
||||
for (freestep = 1; freestep <= 3; freestep++) {
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
malloc_keep_x(x, num, size, freestep);
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
mem_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_mem_one),
|
||||
TESTFUNC(test_mem_random)
|
||||
};
|
||||
return create_suite("MEM", tests, sizeof(tests)/sizeof(testfunc), mem_setup, mem_teardown);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_MEM_H
|
||||
#define LWIP_HDR_TEST_MEM_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *mem_suite(void);
|
||||
|
||||
#endif
|
||||
@@ -1,239 +0,0 @@
|
||||
#include "test_pbuf.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#if !LWIP_STATS || !MEM_STATS ||!MEMP_STATS
|
||||
#error "This tests needs MEM- and MEMP-statistics enabled"
|
||||
#endif
|
||||
#if LWIP_DNS
|
||||
#error "This test needs DNS turned off (as it mallocs on init)"
|
||||
#endif
|
||||
#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !LWIP_WND_SCALE
|
||||
#error "This test needs TCP OOSEQ queueing and window scaling enabled"
|
||||
#endif
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
pbuf_setup(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pbuf_teardown(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#define TESTBUFSIZE_1 65535
|
||||
#define TESTBUFSIZE_2 65530
|
||||
#define TESTBUFSIZE_3 50050
|
||||
static u8_t testbuf_1[TESTBUFSIZE_1];
|
||||
static u8_t testbuf_1a[TESTBUFSIZE_1];
|
||||
static u8_t testbuf_2[TESTBUFSIZE_2];
|
||||
static u8_t testbuf_2a[TESTBUFSIZE_2];
|
||||
static u8_t testbuf_3[TESTBUFSIZE_3];
|
||||
static u8_t testbuf_3a[TESTBUFSIZE_3];
|
||||
|
||||
/* Test functions */
|
||||
|
||||
/** Call pbuf_copy on a pbuf with zero length */
|
||||
START_TEST(test_pbuf_copy_zero_pbuf)
|
||||
{
|
||||
struct pbuf *p1, *p2, *p3;
|
||||
err_t err;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
|
||||
|
||||
p1 = pbuf_alloc(PBUF_RAW, 1024, PBUF_RAM);
|
||||
fail_unless(p1 != NULL);
|
||||
fail_unless(p1->ref == 1);
|
||||
|
||||
p2 = pbuf_alloc(PBUF_RAW, 2, PBUF_POOL);
|
||||
fail_unless(p2 != NULL);
|
||||
fail_unless(p2->ref == 1);
|
||||
p2->len = p2->tot_len = 0;
|
||||
|
||||
pbuf_cat(p1, p2);
|
||||
fail_unless(p1->ref == 1);
|
||||
fail_unless(p2->ref == 1);
|
||||
|
||||
p3 = pbuf_alloc(PBUF_RAW, p1->tot_len, PBUF_POOL);
|
||||
err = pbuf_copy(p3, p1);
|
||||
fail_unless(err == ERR_VAL);
|
||||
|
||||
pbuf_free(p1);
|
||||
pbuf_free(p3);
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_pbuf_split_64k_on_small_pbufs)
|
||||
{
|
||||
struct pbuf *p, *rest=NULL;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, 1, PBUF_POOL);
|
||||
pbuf_split_64k(p, &rest);
|
||||
fail_unless(p->tot_len == 1);
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_pbuf_queueing_bigger_than_64k)
|
||||
{
|
||||
int i;
|
||||
err_t err;
|
||||
struct pbuf *p1, *p2, *p3, *rest2=NULL, *rest3=NULL;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
for(i = 0; i < TESTBUFSIZE_1; i++) {
|
||||
testbuf_1[i] = (u8_t)rand();
|
||||
}
|
||||
for(i = 0; i < TESTBUFSIZE_2; i++) {
|
||||
testbuf_2[i] = (u8_t)rand();
|
||||
}
|
||||
for(i = 0; i < TESTBUFSIZE_3; i++) {
|
||||
testbuf_3[i] = (u8_t)rand();
|
||||
}
|
||||
|
||||
p1 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_1, PBUF_POOL);
|
||||
fail_unless(p1 != NULL);
|
||||
p2 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_2, PBUF_POOL);
|
||||
fail_unless(p2 != NULL);
|
||||
p3 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_3, PBUF_POOL);
|
||||
fail_unless(p3 != NULL);
|
||||
err = pbuf_take(p1, testbuf_1, TESTBUFSIZE_1);
|
||||
fail_unless(err == ERR_OK);
|
||||
err = pbuf_take(p2, testbuf_2, TESTBUFSIZE_2);
|
||||
fail_unless(err == ERR_OK);
|
||||
err = pbuf_take(p3, testbuf_3, TESTBUFSIZE_3);
|
||||
fail_unless(err == ERR_OK);
|
||||
|
||||
pbuf_cat(p1, p2);
|
||||
pbuf_cat(p1, p3);
|
||||
|
||||
pbuf_split_64k(p1, &rest2);
|
||||
fail_unless(p1->tot_len == TESTBUFSIZE_1);
|
||||
fail_unless(rest2->tot_len == (u16_t)((TESTBUFSIZE_2+TESTBUFSIZE_3) & 0xFFFF));
|
||||
pbuf_split_64k(rest2, &rest3);
|
||||
fail_unless(rest2->tot_len == TESTBUFSIZE_2);
|
||||
fail_unless(rest3->tot_len == TESTBUFSIZE_3);
|
||||
|
||||
pbuf_copy_partial(p1, testbuf_1a, TESTBUFSIZE_1, 0);
|
||||
pbuf_copy_partial(rest2, testbuf_2a, TESTBUFSIZE_2, 0);
|
||||
pbuf_copy_partial(rest3, testbuf_3a, TESTBUFSIZE_3, 0);
|
||||
for(i = 0; i < TESTBUFSIZE_1; i++)
|
||||
fail_unless(testbuf_1[i] == testbuf_1a[i]);
|
||||
for(i = 0; i < TESTBUFSIZE_2; i++)
|
||||
fail_unless(testbuf_2[i] == testbuf_2a[i]);
|
||||
for(i = 0; i < TESTBUFSIZE_3; i++)
|
||||
fail_unless(testbuf_3[i] == testbuf_3a[i]);
|
||||
|
||||
pbuf_free(p1);
|
||||
pbuf_free(rest2);
|
||||
pbuf_free(rest3);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Test for bug that writing with pbuf_take_at() did nothing
|
||||
* and returned ERR_OK when writing at beginning of a pbuf
|
||||
* in the chain.
|
||||
*/
|
||||
START_TEST(test_pbuf_take_at_edge)
|
||||
{
|
||||
err_t res;
|
||||
u8_t *out;
|
||||
int i;
|
||||
u8_t testdata[] = { 0x01, 0x08, 0x82, 0x02 };
|
||||
struct pbuf *p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
|
||||
struct pbuf *q = p->next;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
/* alloc big enough to get a chain of pbufs */
|
||||
fail_if(p->tot_len == p->len);
|
||||
memset(p->payload, 0, p->len);
|
||||
memset(q->payload, 0, q->len);
|
||||
|
||||
/* copy data to the beginning of first pbuf */
|
||||
res = pbuf_take_at(p, &testdata, sizeof(testdata), 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
out = (u8_t*)p->payload;
|
||||
for (i = 0; i < (int)sizeof(testdata); i++) {
|
||||
fail_unless(out[i] == testdata[i],
|
||||
"Bad data at pos %d, was %02X, expected %02X", i, out[i], testdata[i]);
|
||||
}
|
||||
|
||||
/* copy data to the just before end of first pbuf */
|
||||
res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len - 1);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
out = (u8_t*)p->payload;
|
||||
fail_unless(out[p->len - 1] == testdata[0],
|
||||
"Bad data at pos %d, was %02X, expected %02X", p->len - 1, out[p->len - 1], testdata[0]);
|
||||
out = (u8_t*)q->payload;
|
||||
for (i = 1; i < (int)sizeof(testdata); i++) {
|
||||
fail_unless(out[i-1] == testdata[i],
|
||||
"Bad data at pos %d, was %02X, expected %02X", p->len - 1 + i, out[i-1], testdata[i]);
|
||||
}
|
||||
|
||||
/* copy data to the beginning of second pbuf */
|
||||
res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
out = (u8_t*)p->payload;
|
||||
for (i = 0; i < (int)sizeof(testdata); i++) {
|
||||
fail_unless(out[i] == testdata[i],
|
||||
"Bad data at pos %d, was %02X, expected %02X", p->len+i, out[i], testdata[i]);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Verify pbuf_put_at()/pbuf_get_at() when using
|
||||
* offsets equal to beginning of new pbuf in chain
|
||||
*/
|
||||
START_TEST(test_pbuf_get_put_at_edge)
|
||||
{
|
||||
u8_t *out;
|
||||
u8_t testdata = 0x01;
|
||||
u8_t getdata;
|
||||
struct pbuf *p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
|
||||
struct pbuf *q = p->next;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
/* alloc big enough to get a chain of pbufs */
|
||||
fail_if(p->tot_len == p->len);
|
||||
memset(p->payload, 0, p->len);
|
||||
memset(q->payload, 0, q->len);
|
||||
|
||||
/* put byte at the beginning of second pbuf */
|
||||
pbuf_put_at(p, p->len, testdata);
|
||||
|
||||
out = (u8_t*)q->payload;
|
||||
fail_unless(*out == testdata,
|
||||
"Bad data at pos %d, was %02X, expected %02X", p->len, *out, testdata);
|
||||
|
||||
getdata = pbuf_get_at(p, p->len);
|
||||
fail_unless(*out == getdata,
|
||||
"pbuf_get_at() returned bad data at pos %d, was %02X, expected %02X", p->len, getdata, *out);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
pbuf_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_pbuf_copy_zero_pbuf),
|
||||
TESTFUNC(test_pbuf_split_64k_on_small_pbufs),
|
||||
TESTFUNC(test_pbuf_queueing_bigger_than_64k),
|
||||
TESTFUNC(test_pbuf_take_at_edge),
|
||||
TESTFUNC(test_pbuf_get_put_at_edge)
|
||||
};
|
||||
return create_suite("PBUF", tests, sizeof(tests)/sizeof(testfunc), pbuf_setup, pbuf_teardown);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_PBUF_H
|
||||
#define LWIP_HDR_TEST_PBUF_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *pbuf_suite(void);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_DHCP_H
|
||||
#define LWIP_HDR_TEST_DHCP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* dhcp_suite(void);
|
||||
|
||||
#endif
|
||||
@@ -1,269 +0,0 @@
|
||||
#include "test_etharp.h"
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/etharp.h"
|
||||
#include "netif/ethernet.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS || !ETHARP_STATS
|
||||
#error "This tests needs UDP-, MEMP- and ETHARP-statistics enabled"
|
||||
#endif
|
||||
#if !ETHARP_SUPPORT_STATIC_ENTRIES
|
||||
#error "This test needs ETHARP_SUPPORT_STATIC_ENTRIES enabled"
|
||||
#endif
|
||||
|
||||
static struct netif test_netif;
|
||||
static ip4_addr_t test_ipaddr, test_netmask, test_gw;
|
||||
struct eth_addr test_ethaddr = {{1,1,1,1,1,1}};
|
||||
struct eth_addr test_ethaddr2 = {{1,1,1,1,1,2}};
|
||||
struct eth_addr test_ethaddr3 = {{1,1,1,1,1,3}};
|
||||
struct eth_addr test_ethaddr4 = {{1,1,1,1,1,4}};
|
||||
static int linkoutput_ctr;
|
||||
|
||||
/* Helper functions */
|
||||
static void
|
||||
etharp_remove_all(void)
|
||||
{
|
||||
int i;
|
||||
/* call etharp_tmr often enough to have all entries cleaned */
|
||||
for(i = 0; i < 0xff; i++) {
|
||||
etharp_tmr();
|
||||
}
|
||||
}
|
||||
|
||||
static err_t
|
||||
default_netif_linkoutput(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
fail_unless(netif == &test_netif);
|
||||
fail_unless(p != NULL);
|
||||
linkoutput_ctr++;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static err_t
|
||||
default_netif_init(struct netif *netif)
|
||||
{
|
||||
fail_unless(netif != NULL);
|
||||
netif->linkoutput = default_netif_linkoutput;
|
||||
netif->output = etharp_output;
|
||||
netif->mtu = 1500;
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
|
||||
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
default_netif_add(void)
|
||||
{
|
||||
IP4_ADDR(&test_gw, 192,168,0,1);
|
||||
IP4_ADDR(&test_ipaddr, 192,168,0,1);
|
||||
IP4_ADDR(&test_netmask, 255,255,0,0);
|
||||
|
||||
fail_unless(netif_default == NULL);
|
||||
netif_set_default(netif_add(&test_netif, &test_ipaddr, &test_netmask,
|
||||
&test_gw, NULL, default_netif_init, NULL));
|
||||
netif_set_up(&test_netif);
|
||||
}
|
||||
|
||||
static void
|
||||
default_netif_remove(void)
|
||||
{
|
||||
fail_unless(netif_default == &test_netif);
|
||||
netif_remove(&test_netif);
|
||||
}
|
||||
|
||||
static void
|
||||
create_arp_response(ip4_addr_t *adr)
|
||||
{
|
||||
int k;
|
||||
struct eth_hdr *ethhdr;
|
||||
struct etharp_hdr *etharphdr;
|
||||
struct pbuf *p = pbuf_alloc(PBUF_RAW, sizeof(struct eth_hdr) + sizeof(struct etharp_hdr), PBUF_RAM);
|
||||
if(p == NULL) {
|
||||
FAIL_RET();
|
||||
}
|
||||
ethhdr = (struct eth_hdr*)p->payload;
|
||||
etharphdr = (struct etharp_hdr*)(ethhdr + 1);
|
||||
|
||||
ethhdr->dest = test_ethaddr;
|
||||
ethhdr->src = test_ethaddr2;
|
||||
ethhdr->type = htons(ETHTYPE_ARP);
|
||||
|
||||
etharphdr->hwtype = htons(/*HWTYPE_ETHERNET*/ 1);
|
||||
etharphdr->proto = htons(ETHTYPE_IP);
|
||||
etharphdr->hwlen = ETHARP_HWADDR_LEN;
|
||||
etharphdr->protolen = sizeof(ip4_addr_t);
|
||||
etharphdr->opcode = htons(ARP_REPLY);
|
||||
|
||||
SMEMCPY(ðarphdr->sipaddr, adr, sizeof(ip4_addr_t));
|
||||
SMEMCPY(ðarphdr->dipaddr, &test_ipaddr, sizeof(ip4_addr_t));
|
||||
|
||||
k = 6;
|
||||
while(k > 0) {
|
||||
k--;
|
||||
/* Write the ARP MAC-Addresses */
|
||||
etharphdr->shwaddr.addr[k] = test_ethaddr2.addr[k];
|
||||
etharphdr->dhwaddr.addr[k] = test_ethaddr.addr[k];
|
||||
/* Write the Ethernet MAC-Addresses */
|
||||
ethhdr->dest.addr[k] = test_ethaddr.addr[k];
|
||||
ethhdr->src.addr[k] = test_ethaddr2.addr[k];
|
||||
}
|
||||
|
||||
ethernet_input(p, &test_netif);
|
||||
}
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
etharp_setup(void)
|
||||
{
|
||||
etharp_remove_all();
|
||||
default_netif_add();
|
||||
}
|
||||
|
||||
static void
|
||||
etharp_teardown(void)
|
||||
{
|
||||
etharp_remove_all();
|
||||
default_netif_remove();
|
||||
}
|
||||
|
||||
|
||||
/* Test functions */
|
||||
|
||||
START_TEST(test_etharp_table)
|
||||
{
|
||||
#if ETHARP_SUPPORT_STATIC_ENTRIES
|
||||
err_t err;
|
||||
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
|
||||
s8_t idx;
|
||||
const ip4_addr_t *unused_ipaddr;
|
||||
struct eth_addr *unused_ethaddr;
|
||||
struct udp_pcb* pcb;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
if (netif_default != &test_netif) {
|
||||
fail("This test needs a default netif");
|
||||
}
|
||||
|
||||
linkoutput_ctr = 0;
|
||||
|
||||
pcb = udp_new();
|
||||
fail_unless(pcb != NULL);
|
||||
if (pcb != NULL) {
|
||||
ip4_addr_t adrs[ARP_TABLE_SIZE + 2];
|
||||
int i;
|
||||
for(i = 0; i < ARP_TABLE_SIZE + 2; i++) {
|
||||
IP4_ADDR(&adrs[i], 192,168,0,i+2);
|
||||
}
|
||||
/* fill ARP-table with dynamic entries */
|
||||
for(i = 0; i < ARP_TABLE_SIZE; i++) {
|
||||
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
|
||||
fail_unless(p != NULL);
|
||||
if (p != NULL) {
|
||||
err_t err2;
|
||||
ip_addr_t dst;
|
||||
ip_addr_copy_from_ip4(dst, adrs[i]);
|
||||
err2 = udp_sendto(pcb, p, &dst, 123);
|
||||
fail_unless(err2 == ERR_OK);
|
||||
/* etharp request sent? */
|
||||
fail_unless(linkoutput_ctr == (2*i) + 1);
|
||||
pbuf_free(p);
|
||||
|
||||
/* create an ARP response */
|
||||
create_arp_response(&adrs[i]);
|
||||
/* queued UDP packet sent? */
|
||||
fail_unless(linkoutput_ctr == (2*i) + 2);
|
||||
|
||||
idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == i);
|
||||
etharp_tmr();
|
||||
}
|
||||
}
|
||||
linkoutput_ctr = 0;
|
||||
#if ETHARP_SUPPORT_STATIC_ENTRIES
|
||||
/* create one static entry */
|
||||
err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE], &test_ethaddr3);
|
||||
fail_unless(err == ERR_OK);
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == 0);
|
||||
fail_unless(linkoutput_ctr == 0);
|
||||
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
linkoutput_ctr = 0;
|
||||
/* fill ARP-table with dynamic entries */
|
||||
for(i = 0; i < ARP_TABLE_SIZE; i++) {
|
||||
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
|
||||
fail_unless(p != NULL);
|
||||
if (p != NULL) {
|
||||
err_t err2;
|
||||
ip_addr_t dst;
|
||||
ip_addr_copy_from_ip4(dst, adrs[i]);
|
||||
err2 = udp_sendto(pcb, p, &dst, 123);
|
||||
fail_unless(err2 == ERR_OK);
|
||||
/* etharp request sent? */
|
||||
fail_unless(linkoutput_ctr == (2*i) + 1);
|
||||
pbuf_free(p);
|
||||
|
||||
/* create an ARP response */
|
||||
create_arp_response(&adrs[i]);
|
||||
/* queued UDP packet sent? */
|
||||
fail_unless(linkoutput_ctr == (2*i) + 2);
|
||||
|
||||
idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
|
||||
if (i < ARP_TABLE_SIZE - 1) {
|
||||
fail_unless(idx == i+1);
|
||||
} else {
|
||||
/* the last entry must not overwrite the static entry! */
|
||||
fail_unless(idx == 1);
|
||||
}
|
||||
etharp_tmr();
|
||||
}
|
||||
}
|
||||
#if ETHARP_SUPPORT_STATIC_ENTRIES
|
||||
/* create a second static entry */
|
||||
err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE+1], &test_ethaddr4);
|
||||
fail_unless(err == ERR_OK);
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == 0);
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == 2);
|
||||
/* and remove it again */
|
||||
err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE+1]);
|
||||
fail_unless(err == ERR_OK);
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == 0);
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == -1);
|
||||
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
/* check that static entries don't time out */
|
||||
etharp_remove_all();
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == 0);
|
||||
|
||||
#if ETHARP_SUPPORT_STATIC_ENTRIES
|
||||
/* remove the first static entry */
|
||||
err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE]);
|
||||
fail_unless(err == ERR_OK);
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == -1);
|
||||
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
|
||||
fail_unless(idx == -1);
|
||||
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
udp_remove(pcb);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
etharp_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_etharp_table)
|
||||
};
|
||||
return create_suite("ETHARP", tests, sizeof(tests)/sizeof(testfunc), etharp_setup, etharp_teardown);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_ETHARP_H
|
||||
#define LWIP_HDR_TEST_ETHARP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* etharp_suite(void);
|
||||
|
||||
#endif
|
||||
@@ -1,154 +0,0 @@
|
||||
#include "test_ip4.h"
|
||||
|
||||
#include "lwip/ip4.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/prot/ip.h"
|
||||
#include "lwip/prot/ip4.h"
|
||||
|
||||
#if !LWIP_IPV4 || !IP_REASSEMBLY || !MIB2_STATS || !IPFRAG_STATS
|
||||
#error "This tests needs LWIP_IPV4, IP_REASSEMBLY; MIB2- and IPFRAG-statistics enabled"
|
||||
#endif
|
||||
|
||||
/* Helper functions */
|
||||
static void
|
||||
create_ip4_input_fragment(u16_t ip_id, u16_t start, u16_t len, int last)
|
||||
{
|
||||
struct pbuf *p;
|
||||
struct netif *input_netif = netif_list; /* just use any netif */
|
||||
fail_unless((start & 7) == 0);
|
||||
fail_unless(((len & 7) == 0) || last);
|
||||
fail_unless(input_netif != NULL);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, len + sizeof(struct ip_hdr), PBUF_RAM);
|
||||
fail_unless(p != NULL);
|
||||
if (p != NULL) {
|
||||
err_t err;
|
||||
struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
|
||||
IPH_VHL_SET(iphdr, 4, sizeof(struct ip_hdr) / 4);
|
||||
IPH_TOS_SET(iphdr, 0);
|
||||
IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
|
||||
IPH_ID_SET(iphdr, lwip_htons(ip_id));
|
||||
if (last) {
|
||||
IPH_OFFSET_SET(iphdr, lwip_htons(start / 8));
|
||||
} else {
|
||||
IPH_OFFSET_SET(iphdr, lwip_htons((start / 8) | IP_MF));
|
||||
}
|
||||
IPH_TTL_SET(iphdr, 5);
|
||||
IPH_PROTO_SET(iphdr, IP_PROTO_UDP);
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
ip4_addr_copy(iphdr->src, *netif_ip4_addr(input_netif));
|
||||
iphdr->src.addr = lwip_htonl(lwip_htonl(iphdr->src.addr) + 1);
|
||||
ip4_addr_copy(iphdr->dest, *netif_ip4_addr(input_netif));
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, sizeof(struct ip_hdr)));
|
||||
|
||||
err = ip4_input(p, input_netif);
|
||||
if (err != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
fail_unless(err == ERR_OK);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
ip4_setup(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
ip4_teardown(void)
|
||||
{
|
||||
if (netif_list->loop_first != NULL) {
|
||||
pbuf_free(netif_list->loop_first);
|
||||
netif_list->loop_first = NULL;
|
||||
}
|
||||
netif_list->loop_last = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Test functions */
|
||||
|
||||
START_TEST(test_ip4_reass)
|
||||
{
|
||||
const u16_t ip_id = 128;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&lwip_stats.mib2, 0, sizeof(lwip_stats.mib2));
|
||||
|
||||
create_ip4_input_fragment(ip_id, 8*200, 200, 1);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 1);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 0*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 2);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 1*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 3);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 2*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 4);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 3*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 5);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 4*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 6);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 7*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 7);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 6*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 8);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
|
||||
|
||||
create_ip4_input_fragment(ip_id, 5*200, 200, 0);
|
||||
fail_unless(lwip_stats.ip_frag.recv == 9);
|
||||
fail_unless(lwip_stats.ip_frag.err == 0);
|
||||
fail_unless(lwip_stats.ip_frag.memerr == 0);
|
||||
fail_unless(lwip_stats.ip_frag.drop == 0);
|
||||
fail_unless(lwip_stats.mib2.ipreasmoks == 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
ip4_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_ip4_reass),
|
||||
};
|
||||
return create_suite("IPv4", tests, sizeof(tests)/sizeof(testfunc), ip4_setup, ip4_teardown);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_IP4_H
|
||||
#define LWIP_HDR_TEST_IP4_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* ip4_suite(void);
|
||||
|
||||
#endif
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef LWIP_HDR_LWIP_CHECK_H
|
||||
#define LWIP_HDR_LWIP_CHECK_H
|
||||
|
||||
/* Common header file for lwIP unit tests using the check framework */
|
||||
|
||||
#include <config.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define FAIL_RET() do { fail(); return; } while(0)
|
||||
#define EXPECT(x) fail_unless(x)
|
||||
#define EXPECT_RET(x) do { fail_unless(x); if(!(x)) { return; }} while(0)
|
||||
#define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0)
|
||||
#define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL)
|
||||
|
||||
typedef struct {
|
||||
TFun func;
|
||||
const char *name;
|
||||
} testfunc;
|
||||
|
||||
#define TESTFUNC(x) {(x), "" # x "" }
|
||||
|
||||
/* Modified function from check.h, supplying function name */
|
||||
#define tcase_add_named_test(tc,tf) \
|
||||
_tcase_add_test((tc),(tf).func,(tf).name,0, 0, 0, 1)
|
||||
|
||||
/** typedef for a function returning a test suite */
|
||||
typedef Suite* (suite_getter_fn)(void);
|
||||
|
||||
/** Create a test suite */
|
||||
Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown);
|
||||
|
||||
#ifdef LWIP_UNITTESTS_LIB
|
||||
int lwip_unittests_run(void)
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_HDR_LWIP_CHECK_H */
|
||||
@@ -1,72 +0,0 @@
|
||||
#include "lwip_check.h"
|
||||
|
||||
#include "ip4/test_ip4.h"
|
||||
#include "udp/test_udp.h"
|
||||
#include "tcp/test_tcp.h"
|
||||
#include "tcp/test_tcp_oos.h"
|
||||
#include "core/test_mem.h"
|
||||
#include "core/test_pbuf.h"
|
||||
#include "etharp/test_etharp.h"
|
||||
#include "dhcp/test_dhcp.h"
|
||||
#include "mdns/test_mdns.h"
|
||||
|
||||
#include "lwip/init.h"
|
||||
|
||||
Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown)
|
||||
{
|
||||
size_t i;
|
||||
Suite *s = suite_create(name);
|
||||
|
||||
for(i = 0; i < num_tests; i++) {
|
||||
TCase *tc_core = tcase_create(name);
|
||||
if ((setup != NULL) || (teardown != NULL)) {
|
||||
tcase_add_checked_fixture(tc_core, setup, teardown);
|
||||
}
|
||||
tcase_add_named_test(tc_core, tests[i]);
|
||||
suite_add_tcase(s, tc_core);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef LWIP_UNITTESTS_LIB
|
||||
int lwip_unittests_run(void)
|
||||
#else
|
||||
int main(void)
|
||||
#endif
|
||||
{
|
||||
int number_failed;
|
||||
SRunner *sr;
|
||||
size_t i;
|
||||
suite_getter_fn* suites[] = {
|
||||
ip4_suite,
|
||||
udp_suite,
|
||||
tcp_suite,
|
||||
tcp_oos_suite,
|
||||
mem_suite,
|
||||
pbuf_suite,
|
||||
etharp_suite,
|
||||
dhcp_suite,
|
||||
mdns_suite
|
||||
};
|
||||
size_t num = sizeof(suites)/sizeof(void*);
|
||||
LWIP_ASSERT("No suites defined", num > 0);
|
||||
|
||||
lwip_init();
|
||||
|
||||
sr = srunner_create((suites[0])());
|
||||
for(i = 1; i < num; i++) {
|
||||
srunner_add_suite(sr, ((suite_getter_fn*)suites[i])());
|
||||
}
|
||||
|
||||
#ifdef LWIP_UNITTESTS_NOFORK
|
||||
srunner_set_fork_status(sr, CK_NOFORK);
|
||||
#endif
|
||||
#ifdef LWIP_UNITTESTS_FORK
|
||||
srunner_set_fork_status(sr, CK_FORK);
|
||||
#endif
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 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: Simon Goldschmidt
|
||||
*
|
||||
*/
|
||||
#ifndef LWIP_HDR_LWIPOPTS_H
|
||||
#define LWIP_HDR_LWIPOPTS_H
|
||||
|
||||
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
|
||||
#define NO_SYS 1
|
||||
#define SYS_LIGHTWEIGHT_PROT 0
|
||||
#define LWIP_NETCONN 0
|
||||
#define LWIP_SOCKET 0
|
||||
|
||||
/* Enable DHCP to test it, disable UDP checksum to easier inject packets */
|
||||
#define LWIP_DHCP 1
|
||||
|
||||
/* Minimal changes to opt.h required for tcp unit tests: */
|
||||
#define MEM_SIZE 16000
|
||||
#define TCP_SND_QUEUELEN 40
|
||||
#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
|
||||
#define TCP_SND_BUF (12 * TCP_MSS)
|
||||
#define TCP_WND (10 * TCP_MSS)
|
||||
#define LWIP_WND_SCALE 1
|
||||
#define TCP_RCV_SCALE 0
|
||||
#define PBUF_POOL_SIZE 400 /* pbuf tests need ~200KByte */
|
||||
|
||||
/* Enable IGMP and MDNS for MDNS tests */
|
||||
#define LWIP_IGMP 1
|
||||
#define LWIP_MDNS_RESPONDER 1
|
||||
#define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER)
|
||||
|
||||
/* Minimal changes to opt.h required for etharp unit tests: */
|
||||
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
|
||||
|
||||
/* MIB2 stats are required to check IPv4 reassembly results */
|
||||
#define MIB2_STATS 1
|
||||
|
||||
#endif /* LWIP_HDR_LWIPOPTS_H */
|
||||
@@ -1,915 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Verisure Innovation AB
|
||||
* 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: Erik Ekman <erik@kryo.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "test_mdns.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/apps/mdns.h"
|
||||
#include "lwip/apps/mdns_priv.h"
|
||||
|
||||
START_TEST(readname_basic)
|
||||
{
|
||||
static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == sizeof(data));
|
||||
fail_unless(domain.length == sizeof(data));
|
||||
fail_if(memcmp(&domain.name, data, sizeof(data)));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_anydata)
|
||||
{
|
||||
static const u8_t data[] = { 0x05, 0x00, 0xFF, 0x08, 0xc0, 0x0f, 0x04, 0x7f, 0x80, 0x82, 0x88, 0x00 };
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == sizeof(data));
|
||||
fail_unless(domain.length == sizeof(data));
|
||||
fail_if(memcmp(&domain.name, data, sizeof(data)));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_short_buf)
|
||||
{
|
||||
static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a' };
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == MDNS_READNAME_ERROR);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_long_label)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i',
|
||||
0x52, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
|
||||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
|
||||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
|
||||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
|
||||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
|
||||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == MDNS_READNAME_ERROR);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_overflow)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == MDNS_READNAME_ERROR);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_jump_earlier)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* Some padding needed, not supported to jump to bytes containing dns header */
|
||||
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 10 */ 0x0f, 0x0e, 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab,
|
||||
/* 20 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x0c
|
||||
};
|
||||
static const u8_t fullname[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 20, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == sizeof(data));
|
||||
fail_unless(domain.length == sizeof(fullname));
|
||||
|
||||
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_jump_earlier_jump)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* Some padding needed, not supported to jump to bytes containing dns header */
|
||||
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
|
||||
/* 0x10 */ 0x04, 'c', 'a', 's', 't', 0x00, 0xc0, 0x10,
|
||||
/* 0x18 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x16
|
||||
};
|
||||
static const u8_t fullname[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0x18, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == sizeof(data));
|
||||
fail_unless(domain.length == sizeof(fullname));
|
||||
|
||||
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_jump_maxdepth)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* Some padding needed, not supported to jump to bytes containing dns header */
|
||||
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
|
||||
/* 0x10 */ 0x04, 'n', 'a', 'm', 'e', 0xc0, 0x27, 0x03,
|
||||
/* 0x18 */ 0x03, 'd', 'n', 's', 0xc0, 0x10, 0xc0, 0x10,
|
||||
/* 0x20 */ 0x04, 'd', 'e', 'e', 'p', 0xc0, 0x18, 0x00,
|
||||
/* 0x28 */ 0x04, 'c', 'a', 's', 't', 0xc0, 0x20, 0xb0,
|
||||
/* 0x30 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x28
|
||||
};
|
||||
static const u8_t fullname[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
|
||||
0x04, 'd', 'e', 'e', 'p', 0x03, 'd', 'n', 's',
|
||||
0x04, 'n', 'a', 'm', 'e', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0x30, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == sizeof(data));
|
||||
fail_unless(domain.length == sizeof(fullname));
|
||||
|
||||
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_jump_later)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* 0x00 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10, 0x00, 0x01, 0x40,
|
||||
/* 0x10 */ 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab
|
||||
};
|
||||
static const u8_t fullname[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == 13);
|
||||
fail_unless(domain.length == sizeof(fullname));
|
||||
|
||||
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_half_jump)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == MDNS_READNAME_ERROR);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_jump_toolong)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc2, 0x10, 0x00, 0x01, 0x40
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 0, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == MDNS_READNAME_ERROR);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_jump_loop_label)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 10, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == MDNS_READNAME_ERROR);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(readname_jump_loop_jump)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x15
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
offset = mdns_readname(p, 10, &domain);
|
||||
pbuf_free(p);
|
||||
fail_unless(offset == MDNS_READNAME_ERROR);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(add_label_basic)
|
||||
{
|
||||
static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
|
||||
struct mdns_domain domain;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == sizeof(data));
|
||||
fail_if(memcmp(&domain.name, data, sizeof(data)));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(add_label_long_label)
|
||||
{
|
||||
static const char *toolong = "abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-";
|
||||
struct mdns_domain domain;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, toolong, (u8_t)strlen(toolong));
|
||||
fail_unless(res == ERR_VAL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(add_label_full)
|
||||
{
|
||||
static const char *label = "0123456789abcdef0123456789abcdef";
|
||||
struct mdns_domain domain;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 33);
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 66);
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 99);
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 132);
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 165);
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 198);
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 231);
|
||||
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
|
||||
fail_unless(res == ERR_VAL);
|
||||
fail_unless(domain.length == 231);
|
||||
res = mdns_domain_add_label(&domain, label, 25);
|
||||
fail_unless(res == ERR_VAL);
|
||||
fail_unless(domain.length == 231);
|
||||
res = mdns_domain_add_label(&domain, label, 24);
|
||||
fail_unless(res == ERR_VAL);
|
||||
fail_unless(domain.length == 231);
|
||||
res = mdns_domain_add_label(&domain, label, 23);
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 255);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain.length == 256);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_VAL);
|
||||
fail_unless(domain.length == 256);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(domain_eq_basic)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
|
||||
};
|
||||
struct mdns_domain domain1, domain2;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain1, 0, sizeof(domain1));
|
||||
res = mdns_domain_add_label(&domain1, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
fail_unless(domain1.length == sizeof(data));
|
||||
|
||||
memset(&domain2, 0, sizeof(domain2));
|
||||
res = mdns_domain_add_label(&domain2, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
fail_unless(mdns_domain_eq(&domain1, &domain2));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(domain_eq_diff)
|
||||
{
|
||||
struct mdns_domain domain1, domain2;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain1, 0, sizeof(domain1));
|
||||
res = mdns_domain_add_label(&domain1, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, "base", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
memset(&domain2, 0, sizeof(domain2));
|
||||
res = mdns_domain_add_label(&domain2, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
fail_if(mdns_domain_eq(&domain1, &domain2));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(domain_eq_case)
|
||||
{
|
||||
struct mdns_domain domain1, domain2;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain1, 0, sizeof(domain1));
|
||||
res = mdns_domain_add_label(&domain1, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
memset(&domain2, 0, sizeof(domain2));
|
||||
res = mdns_domain_add_label(&domain2, "MulTI", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, "casT", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
fail_unless(mdns_domain_eq(&domain1, &domain2));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(domain_eq_anydata)
|
||||
{
|
||||
static const u8_t data1[] = { 0x05, 0xcc, 0xdc, 0x00, 0xa0 };
|
||||
static const u8_t data2[] = { 0x7f, 0x8c, 0x01, 0xff, 0xcf };
|
||||
struct mdns_domain domain1, domain2;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain1, 0, sizeof(domain1));
|
||||
res = mdns_domain_add_label(&domain1, (const char*)data1, sizeof(data1));
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, (const char*)data2, sizeof(data2));
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
memset(&domain2, 0, sizeof(domain2));
|
||||
res = mdns_domain_add_label(&domain2, (const char*)data1, sizeof(data1));
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, "casT", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, (const char*)data2, sizeof(data2));
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
fail_unless(mdns_domain_eq(&domain1, &domain2));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(domain_eq_length)
|
||||
{
|
||||
struct mdns_domain domain1, domain2;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&domain1, 0, sizeof(domain1));
|
||||
memset(domain1.name, 0xAA, sizeof(MDNS_DOMAIN_MAXLEN));
|
||||
res = mdns_domain_add_label(&domain1, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain1, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
memset(&domain2, 0, sizeof(domain2));
|
||||
memset(domain2.name, 0xBB, sizeof(MDNS_DOMAIN_MAXLEN));
|
||||
res = mdns_domain_add_label(&domain2, "multi", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain2, "cast", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
fail_unless(mdns_domain_eq(&domain1, &domain2));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_full_match)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x00, 0x00,
|
||||
0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "foobar", 6);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 2;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Write 0 bytes, then a jump to addr 2 */
|
||||
fail_unless(length == 0);
|
||||
fail_unless(offset == 2);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_full_match_subset)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x00, 0x00,
|
||||
0x02, 'g', 'o', 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "foobar", 6);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 2;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Write 0 bytes, then a jump to addr 5 */
|
||||
fail_unless(length == 0);
|
||||
fail_unless(offset == 5);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_full_match_jump)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
/* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
|
||||
/* 0x20 */ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0xc0, 0x15
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "foobar", 6);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 0x20;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Write 0 bytes, then a jump to addr 0x20 */
|
||||
fail_unless(length == 0);
|
||||
fail_unless(offset == 0x20);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_no_match)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x00, 0x00,
|
||||
0x04, 'l', 'w', 'i', 'p', 0x05, 'w', 'i', 'k', 'i', 'a', 0x03, 'c', 'o', 'm', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "foobar", 6);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 2;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Write all bytes, no jump */
|
||||
fail_unless(length == domain.length);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_2nd_label)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x00, 0x00,
|
||||
0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "lwip", 4);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 2;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Write 5 bytes, then a jump to addr 9 */
|
||||
fail_unless(length == 5);
|
||||
fail_unless(offset == 9);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_2nd_label_short)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x00, 0x00,
|
||||
0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "foobar", 6);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 2;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Write 5 bytes, then a jump to addr 7 */
|
||||
fail_unless(length == 7);
|
||||
fail_unless(offset == 7);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_jump_to_jump)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
/* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
/* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
|
||||
/* 0x20 */ 0x07, 'b', 'a', 'n', 'a', 'n', 'a', 's', 0xc0, 0x15
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "foobar", 6);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 0x20;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Dont compress if jump would be to a jump */
|
||||
fail_unless(length == domain.length);
|
||||
|
||||
offset = 0x10;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
/* Write 7 bytes, then a jump to addr 0x15 */
|
||||
fail_unless(length == 7);
|
||||
fail_unless(offset == 0x15);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(compress_long_match)
|
||||
{
|
||||
static const u8_t data[] = {
|
||||
0x00, 0x00,
|
||||
0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x03, 'c', 'o', 'm', 0x00
|
||||
};
|
||||
struct pbuf *p;
|
||||
struct mdns_domain domain;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
err_t res;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
|
||||
p->payload = (void *)(size_t)data;
|
||||
fail_if(p == NULL);
|
||||
|
||||
memset(&domain, 0, sizeof(domain));
|
||||
res = mdns_domain_add_label(&domain, "foobar", 6);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, "local", 5);
|
||||
fail_unless(res == ERR_OK);
|
||||
res = mdns_domain_add_label(&domain, NULL, 0);
|
||||
fail_unless(res == ERR_OK);
|
||||
|
||||
offset = 2;
|
||||
length = mdns_compress_domain(p, &offset, &domain);
|
||||
fail_unless(length == domain.length);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite* mdns_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(readname_basic),
|
||||
TESTFUNC(readname_anydata),
|
||||
TESTFUNC(readname_short_buf),
|
||||
TESTFUNC(readname_long_label),
|
||||
TESTFUNC(readname_overflow),
|
||||
TESTFUNC(readname_jump_earlier),
|
||||
TESTFUNC(readname_jump_earlier_jump),
|
||||
TESTFUNC(readname_jump_maxdepth),
|
||||
TESTFUNC(readname_jump_later),
|
||||
TESTFUNC(readname_half_jump),
|
||||
TESTFUNC(readname_jump_toolong),
|
||||
TESTFUNC(readname_jump_loop_label),
|
||||
TESTFUNC(readname_jump_loop_jump),
|
||||
|
||||
TESTFUNC(add_label_basic),
|
||||
TESTFUNC(add_label_long_label),
|
||||
TESTFUNC(add_label_full),
|
||||
|
||||
TESTFUNC(domain_eq_basic),
|
||||
TESTFUNC(domain_eq_diff),
|
||||
TESTFUNC(domain_eq_case),
|
||||
TESTFUNC(domain_eq_anydata),
|
||||
TESTFUNC(domain_eq_length),
|
||||
|
||||
TESTFUNC(compress_full_match),
|
||||
TESTFUNC(compress_full_match_subset),
|
||||
TESTFUNC(compress_full_match_jump),
|
||||
TESTFUNC(compress_no_match),
|
||||
TESTFUNC(compress_2nd_label),
|
||||
TESTFUNC(compress_2nd_label_short),
|
||||
TESTFUNC(compress_jump_to_jump),
|
||||
TESTFUNC(compress_long_match),
|
||||
};
|
||||
return create_suite("MDNS", tests, sizeof(tests)/sizeof(testfunc), NULL, NULL);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_MDNS_H__
|
||||
#define LWIP_HDR_TEST_MDNS_H__
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* mdns_suite(void);
|
||||
|
||||
#endif
|
||||
@@ -1,314 +0,0 @@
|
||||
#include "tcp_helper.h"
|
||||
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
|
||||
#error "This tests needs TCP- and MEMP-statistics enabled"
|
||||
#endif
|
||||
|
||||
/** Remove all pcbs on the given list. */
|
||||
static void
|
||||
tcp_remove(struct tcp_pcb* pcb_list)
|
||||
{
|
||||
struct tcp_pcb *pcb = pcb_list;
|
||||
struct tcp_pcb *pcb2;
|
||||
|
||||
while(pcb != NULL) {
|
||||
pcb2 = pcb;
|
||||
pcb = pcb->next;
|
||||
tcp_abort(pcb2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */
|
||||
void
|
||||
tcp_remove_all(void)
|
||||
{
|
||||
tcp_remove(tcp_listen_pcbs.pcbs);
|
||||
tcp_remove(tcp_active_pcbs);
|
||||
tcp_remove(tcp_tw_pcbs);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 0);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_SEG) == 0);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
|
||||
}
|
||||
|
||||
/** Create a TCP segment usable for passing to tcp_input */
|
||||
static struct pbuf*
|
||||
tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
|
||||
u16_t src_port, u16_t dst_port, void* data, size_t data_len,
|
||||
u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd)
|
||||
{
|
||||
struct pbuf *p, *q;
|
||||
struct ip_hdr* iphdr;
|
||||
struct tcp_hdr* tcphdr;
|
||||
u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len);
|
||||
LWIP_ASSERT("data_len too big", data_len <= 0xFFFF);
|
||||
|
||||
p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL);
|
||||
EXPECT_RETNULL(p != NULL);
|
||||
/* first pbuf must be big enough to hold the headers */
|
||||
EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
|
||||
if (data_len > 0) {
|
||||
/* first pbuf must be big enough to hold at least 1 data byte, too */
|
||||
EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
|
||||
}
|
||||
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
memset(q->payload, 0, q->len);
|
||||
}
|
||||
|
||||
iphdr = (struct ip_hdr*)p->payload;
|
||||
/* fill IP header */
|
||||
iphdr->dest.addr = ip_2_ip4(dst_ip)->addr;
|
||||
iphdr->src.addr = ip_2_ip4(src_ip)->addr;
|
||||
IPH_VHL_SET(iphdr, 4, IP_HLEN / 4);
|
||||
IPH_TOS_SET(iphdr, 0);
|
||||
IPH_LEN_SET(iphdr, htons(p->tot_len));
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
||||
|
||||
/* let p point to TCP header */
|
||||
pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
|
||||
|
||||
tcphdr = (struct tcp_hdr*)p->payload;
|
||||
tcphdr->src = htons(src_port);
|
||||
tcphdr->dest = htons(dst_port);
|
||||
tcphdr->seqno = htonl(seqno);
|
||||
tcphdr->ackno = htonl(ackno);
|
||||
TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4);
|
||||
TCPH_FLAGS_SET(tcphdr, headerflags);
|
||||
tcphdr->wnd = htons(wnd);
|
||||
|
||||
if (data_len > 0) {
|
||||
/* let p point to TCP data */
|
||||
pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr));
|
||||
/* copy data */
|
||||
pbuf_take(p, data, (u16_t)data_len);
|
||||
/* let p point to TCP header again */
|
||||
pbuf_header(p, sizeof(struct tcp_hdr));
|
||||
}
|
||||
|
||||
/* calculate checksum */
|
||||
|
||||
tcphdr->chksum = ip_chksum_pseudo(p,
|
||||
IP_PROTO_TCP, p->tot_len, src_ip, dst_ip);
|
||||
|
||||
pbuf_header(p, sizeof(struct ip_hdr));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Create a TCP segment usable for passing to tcp_input */
|
||||
struct pbuf*
|
||||
tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
|
||||
u16_t src_port, u16_t dst_port, void* data, size_t data_len,
|
||||
u32_t seqno, u32_t ackno, u8_t headerflags)
|
||||
{
|
||||
return tcp_create_segment_wnd(src_ip, dst_ip, src_port, dst_port, data,
|
||||
data_len, seqno, ackno, headerflags, TCP_WND);
|
||||
}
|
||||
|
||||
/** Create a TCP segment usable for passing to tcp_input
|
||||
* - IP-addresses, ports, seqno and ackno are taken from pcb
|
||||
* - seqno and ackno can be altered with an offset
|
||||
*/
|
||||
struct pbuf*
|
||||
tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t seqno_offset,
|
||||
u32_t ackno_offset, u8_t headerflags)
|
||||
{
|
||||
return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
|
||||
data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags);
|
||||
}
|
||||
|
||||
/** Create a TCP segment usable for passing to tcp_input
|
||||
* - IP-addresses, ports, seqno and ackno are taken from pcb
|
||||
* - seqno and ackno can be altered with an offset
|
||||
* - TCP window can be adjusted
|
||||
*/
|
||||
struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
|
||||
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd)
|
||||
{
|
||||
return tcp_create_segment_wnd(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
|
||||
data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags, wnd);
|
||||
}
|
||||
|
||||
/** Safely bring a tcp_pcb into the requested state */
|
||||
void
|
||||
tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
|
||||
ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
|
||||
{
|
||||
u32_t iss;
|
||||
|
||||
/* @todo: are these all states? */
|
||||
/* @todo: remove from previous list */
|
||||
pcb->state = state;
|
||||
|
||||
iss = tcp_next_iss(pcb);
|
||||
pcb->snd_wl2 = iss;
|
||||
pcb->snd_nxt = iss;
|
||||
pcb->lastack = iss;
|
||||
pcb->snd_lbb = iss;
|
||||
|
||||
if (state == ESTABLISHED) {
|
||||
TCP_REG(&tcp_active_pcbs, pcb);
|
||||
ip_addr_copy(pcb->local_ip, *local_ip);
|
||||
pcb->local_port = local_port;
|
||||
ip_addr_copy(pcb->remote_ip, *remote_ip);
|
||||
pcb->remote_port = remote_port;
|
||||
} else if(state == LISTEN) {
|
||||
TCP_REG(&tcp_listen_pcbs.pcbs, pcb);
|
||||
ip_addr_copy(pcb->local_ip, *local_ip);
|
||||
pcb->local_port = local_port;
|
||||
} else if(state == TIME_WAIT) {
|
||||
TCP_REG(&tcp_tw_pcbs, pcb);
|
||||
ip_addr_copy(pcb->local_ip, *local_ip);
|
||||
pcb->local_port = local_port;
|
||||
ip_addr_copy(pcb->remote_ip, *remote_ip);
|
||||
pcb->remote_port = remote_port;
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_tcp_counters_err(void* arg, err_t err)
|
||||
{
|
||||
struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
|
||||
EXPECT_RET(arg != NULL);
|
||||
counters->err_calls++;
|
||||
counters->last_err = err;
|
||||
}
|
||||
|
||||
static void
|
||||
test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf* p)
|
||||
{
|
||||
struct pbuf* q;
|
||||
u32_t i, received;
|
||||
if(counters->expected_data == NULL) {
|
||||
/* no data to compare */
|
||||
return;
|
||||
}
|
||||
EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len);
|
||||
received = counters->recved_bytes;
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
char *data = (char*)q->payload;
|
||||
for(i = 0; i < q->len; i++) {
|
||||
EXPECT_RET(data[i] == counters->expected_data[received]);
|
||||
received++;
|
||||
}
|
||||
}
|
||||
EXPECT(received == counters->recved_bytes + p->tot_len);
|
||||
}
|
||||
|
||||
err_t
|
||||
test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err)
|
||||
{
|
||||
struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
|
||||
EXPECT_RETX(arg != NULL, ERR_OK);
|
||||
EXPECT_RETX(pcb != NULL, ERR_OK);
|
||||
EXPECT_RETX(err == ERR_OK, ERR_OK);
|
||||
|
||||
if (p != NULL) {
|
||||
if (counters->close_calls == 0) {
|
||||
counters->recv_calls++;
|
||||
test_tcp_counters_check_rxdata(counters, p);
|
||||
counters->recved_bytes += p->tot_len;
|
||||
} else {
|
||||
counters->recv_calls_after_close++;
|
||||
counters->recved_bytes_after_close += p->tot_len;
|
||||
}
|
||||
pbuf_free(p);
|
||||
} else {
|
||||
counters->close_calls++;
|
||||
}
|
||||
EXPECT(counters->recv_calls_after_close == 0 && counters->recved_bytes_after_close == 0);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** Allocate a pcb and set up the test_tcp_counters_* callbacks */
|
||||
struct tcp_pcb*
|
||||
test_tcp_new_counters_pcb(struct test_tcp_counters* counters)
|
||||
{
|
||||
struct tcp_pcb* pcb = tcp_new();
|
||||
if (pcb != NULL) {
|
||||
/* set up args and callbacks */
|
||||
tcp_arg(pcb, counters);
|
||||
tcp_recv(pcb, test_tcp_counters_recv);
|
||||
tcp_err(pcb, test_tcp_counters_err);
|
||||
pcb->snd_wnd = TCP_WND;
|
||||
pcb->snd_wnd_max = TCP_WND;
|
||||
}
|
||||
return pcb;
|
||||
}
|
||||
|
||||
/** Calls tcp_input() after adjusting current_iphdr_dest */
|
||||
void test_tcp_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
|
||||
/* these lines are a hack, don't use them as an example :-) */
|
||||
ip_addr_copy_from_ip4(*ip_current_dest_addr(), iphdr->dest);
|
||||
ip_addr_copy_from_ip4(*ip_current_src_addr(), iphdr->src);
|
||||
ip_current_netif() = inp;
|
||||
ip_data.current_ip4_header = iphdr;
|
||||
|
||||
/* since adding IPv6, p->payload must point to tcp header, not ip header */
|
||||
pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
|
||||
|
||||
tcp_input(p, inp);
|
||||
|
||||
ip_addr_set_zero(ip_current_dest_addr());
|
||||
ip_addr_set_zero(ip_current_src_addr());
|
||||
ip_current_netif() = NULL;
|
||||
ip_data.current_ip4_header = NULL;
|
||||
}
|
||||
|
||||
static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p,
|
||||
const ip4_addr_t *ipaddr)
|
||||
{
|
||||
struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state;
|
||||
LWIP_UNUSED_ARG(ipaddr);
|
||||
if (txcounters != NULL)
|
||||
{
|
||||
txcounters->num_tx_calls++;
|
||||
txcounters->num_tx_bytes += p->tot_len;
|
||||
if (txcounters->copy_tx_packets) {
|
||||
struct pbuf *p_copy = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
|
||||
err_t err;
|
||||
EXPECT(p_copy != NULL);
|
||||
err = pbuf_copy(p_copy, p);
|
||||
EXPECT(err == ERR_OK);
|
||||
if (txcounters->tx_packets == NULL) {
|
||||
txcounters->tx_packets = p_copy;
|
||||
} else {
|
||||
pbuf_cat(txcounters->tx_packets, p_copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
|
||||
ip_addr_t *ip_addr, ip_addr_t *netmask)
|
||||
{
|
||||
struct netif *n;
|
||||
memset(netif, 0, sizeof(struct netif));
|
||||
if (txcounters != NULL) {
|
||||
memset(txcounters, 0, sizeof(struct test_tcp_txcounters));
|
||||
netif->state = txcounters;
|
||||
}
|
||||
netif->output = test_tcp_netif_output;
|
||||
netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP;
|
||||
ip_addr_copy_from_ip4(netif->netmask, *ip_2_ip4(netmask));
|
||||
ip_addr_copy_from_ip4(netif->ip_addr, *ip_2_ip4(ip_addr));
|
||||
for (n = netif_list; n != NULL; n = n->next) {
|
||||
if (n == netif) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
netif->next = NULL;
|
||||
netif_list = netif;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef LWIP_HDR_TCP_HELPER_H
|
||||
#define LWIP_HDR_TCP_HELPER_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
#include "lwip/arch.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
/* counters used for test_tcp_counters_* callback functions */
|
||||
struct test_tcp_counters {
|
||||
u32_t recv_calls;
|
||||
u32_t recved_bytes;
|
||||
u32_t recv_calls_after_close;
|
||||
u32_t recved_bytes_after_close;
|
||||
u32_t close_calls;
|
||||
u32_t err_calls;
|
||||
err_t last_err;
|
||||
char* expected_data;
|
||||
u32_t expected_data_len;
|
||||
};
|
||||
|
||||
struct test_tcp_txcounters {
|
||||
u32_t num_tx_calls;
|
||||
u32_t num_tx_bytes;
|
||||
u8_t copy_tx_packets;
|
||||
struct pbuf *tx_packets;
|
||||
};
|
||||
|
||||
/* Helper functions */
|
||||
void tcp_remove_all(void);
|
||||
|
||||
struct pbuf* tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
|
||||
u16_t src_port, u16_t dst_port, void* data, size_t data_len,
|
||||
u32_t seqno, u32_t ackno, u8_t headerflags);
|
||||
struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len,
|
||||
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags);
|
||||
struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
|
||||
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd);
|
||||
void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
|
||||
ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port);
|
||||
void test_tcp_counters_err(void* arg, err_t err);
|
||||
err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
|
||||
|
||||
struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters);
|
||||
|
||||
void test_tcp_input(struct pbuf *p, struct netif *inp);
|
||||
|
||||
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
|
||||
ip_addr_t *ip_addr, ip_addr_t *netmask);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,744 +0,0 @@
|
||||
#include "test_tcp.h"
|
||||
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "tcp_helper.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */
|
||||
#endif
|
||||
|
||||
#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
|
||||
#error "This tests needs TCP- and MEMP-statistics enabled"
|
||||
#endif
|
||||
#if TCP_SND_BUF <= TCP_WND
|
||||
#error "This tests needs TCP_SND_BUF to be > TCP_WND"
|
||||
#endif
|
||||
|
||||
static u8_t test_tcp_timer;
|
||||
|
||||
/* our own version of tcp_tmr so we can reset fast/slow timer state */
|
||||
static void
|
||||
test_tcp_tmr(void)
|
||||
{
|
||||
tcp_fasttmr();
|
||||
if (++test_tcp_timer & 1) {
|
||||
tcp_slowtmr();
|
||||
}
|
||||
}
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
tcp_setup(void)
|
||||
{
|
||||
/* reset iss to default (6510) */
|
||||
tcp_ticks = 0;
|
||||
tcp_ticks = 0 - (tcp_next_iss(NULL) - 6510);
|
||||
tcp_next_iss(NULL);
|
||||
tcp_ticks = 0;
|
||||
|
||||
test_tcp_timer = 0;
|
||||
tcp_remove_all();
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_teardown(void)
|
||||
{
|
||||
netif_list = NULL;
|
||||
netif_default = NULL;
|
||||
tcp_remove_all();
|
||||
}
|
||||
|
||||
|
||||
/* Test functions */
|
||||
|
||||
/** Call tcp_new() and tcp_abort() and test memp stats */
|
||||
START_TEST(test_tcp_new_abort)
|
||||
{
|
||||
struct tcp_pcb* pcb;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
|
||||
pcb = tcp_new();
|
||||
fail_unless(pcb != NULL);
|
||||
if (pcb != NULL) {
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
|
||||
tcp_abort(pcb);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Create an ESTABLISHED pcb and check if receive callback is called */
|
||||
START_TEST(test_tcp_recv_inseq)
|
||||
{
|
||||
struct test_tcp_counters counters;
|
||||
struct tcp_pcb* pcb;
|
||||
struct pbuf* p;
|
||||
char data[] = {1, 2, 3, 4};
|
||||
ip_addr_t remote_ip, local_ip, netmask;
|
||||
u16_t data_len;
|
||||
u16_t remote_port = 0x100, local_port = 0x101;
|
||||
struct netif netif;
|
||||
struct test_tcp_txcounters txcounters;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
/* initialize local vars */
|
||||
memset(&netif, 0, sizeof(netif));
|
||||
IP_ADDR4(&local_ip, 192, 168, 1, 1);
|
||||
IP_ADDR4(&remote_ip, 192, 168, 1, 2);
|
||||
IP_ADDR4(&netmask, 255, 255, 255, 0);
|
||||
test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
|
||||
data_len = sizeof(data);
|
||||
/* initialize counter struct */
|
||||
memset(&counters, 0, sizeof(counters));
|
||||
counters.expected_data_len = data_len;
|
||||
counters.expected_data = data;
|
||||
|
||||
/* create and initialize the pcb */
|
||||
pcb = test_tcp_new_counters_pcb(&counters);
|
||||
EXPECT_RET(pcb != NULL);
|
||||
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
|
||||
|
||||
/* create a segment */
|
||||
p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
|
||||
EXPECT(p != NULL);
|
||||
if (p != NULL) {
|
||||
/* pass the segment to tcp_input */
|
||||
test_tcp_input(p, &netif);
|
||||
/* check if counters are as expected */
|
||||
EXPECT(counters.close_calls == 0);
|
||||
EXPECT(counters.recv_calls == 1);
|
||||
EXPECT(counters.recved_bytes == data_len);
|
||||
EXPECT(counters.err_calls == 0);
|
||||
}
|
||||
|
||||
/* make sure the pcb is freed */
|
||||
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
|
||||
tcp_abort(pcb);
|
||||
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Check that we handle malformed tcp headers, and discard the pbuf(s) */
|
||||
START_TEST(test_tcp_malformed_header)
|
||||
{
|
||||
struct test_tcp_counters counters;
|
||||
struct tcp_pcb* pcb;
|
||||
struct pbuf* p;
|
||||
char data[] = {1, 2, 3, 4};
|
||||
ip_addr_t remote_ip, local_ip, netmask;
|
||||
u16_t data_len, chksum;
|
||||
u16_t remote_port = 0x100, local_port = 0x101;
|
||||
struct netif netif;
|
||||
struct test_tcp_txcounters txcounters;
|
||||
struct tcp_hdr *hdr;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
/* initialize local vars */
|
||||
memset(&netif, 0, sizeof(netif));
|
||||
IP_ADDR4(&local_ip, 192, 168, 1, 1);
|
||||
IP_ADDR4(&remote_ip, 192, 168, 1, 2);
|
||||
IP_ADDR4(&netmask, 255, 255, 255, 0);
|
||||
test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
|
||||
data_len = sizeof(data);
|
||||
/* initialize counter struct */
|
||||
memset(&counters, 0, sizeof(counters));
|
||||
counters.expected_data_len = data_len;
|
||||
counters.expected_data = data;
|
||||
|
||||
/* create and initialize the pcb */
|
||||
pcb = test_tcp_new_counters_pcb(&counters);
|
||||
EXPECT_RET(pcb != NULL);
|
||||
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
|
||||
|
||||
/* create a segment */
|
||||
p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
|
||||
|
||||
pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
|
||||
|
||||
hdr = (struct tcp_hdr *)p->payload;
|
||||
TCPH_HDRLEN_FLAGS_SET(hdr, 15, 0x3d1);
|
||||
|
||||
hdr->chksum = 0;
|
||||
|
||||
chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
|
||||
&remote_ip, &local_ip);
|
||||
|
||||
hdr->chksum = chksum;
|
||||
|
||||
pbuf_header(p, sizeof(struct ip_hdr));
|
||||
|
||||
EXPECT(p != NULL);
|
||||
EXPECT(p->next == NULL);
|
||||
if (p != NULL) {
|
||||
/* pass the segment to tcp_input */
|
||||
test_tcp_input(p, &netif);
|
||||
/* check if counters are as expected */
|
||||
EXPECT(counters.close_calls == 0);
|
||||
EXPECT(counters.recv_calls == 0);
|
||||
EXPECT(counters.recved_bytes == 0);
|
||||
EXPECT(counters.err_calls == 0);
|
||||
}
|
||||
|
||||
/* make sure the pcb is freed */
|
||||
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
|
||||
tcp_abort(pcb);
|
||||
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
|
||||
* At the end, send more data. */
|
||||
START_TEST(test_tcp_fast_retx_recover)
|
||||
{
|
||||
struct netif netif;
|
||||
struct test_tcp_txcounters txcounters;
|
||||
struct test_tcp_counters counters;
|
||||
struct tcp_pcb* pcb;
|
||||
struct pbuf* p;
|
||||
char data1[] = { 1, 2, 3, 4};
|
||||
char data2[] = { 5, 6, 7, 8};
|
||||
char data3[] = { 9, 10, 11, 12};
|
||||
char data4[] = {13, 14, 15, 16};
|
||||
char data5[] = {17, 18, 19, 20};
|
||||
char data6[TCP_MSS] = {21, 22, 23, 24};
|
||||
ip_addr_t remote_ip, local_ip, netmask;
|
||||
u16_t remote_port = 0x100, local_port = 0x101;
|
||||
err_t err;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
/* initialize local vars */
|
||||
IP_ADDR4(&local_ip, 192, 168, 1, 1);
|
||||
IP_ADDR4(&remote_ip, 192, 168, 1, 2);
|
||||
IP_ADDR4(&netmask, 255, 255, 255, 0);
|
||||
test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
|
||||
memset(&counters, 0, sizeof(counters));
|
||||
|
||||
/* create and initialize the pcb */
|
||||
pcb = test_tcp_new_counters_pcb(&counters);
|
||||
EXPECT_RET(pcb != NULL);
|
||||
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
|
||||
pcb->mss = TCP_MSS;
|
||||
/* disable initial congestion window (we don't send a SYN here...) */
|
||||
pcb->cwnd = pcb->snd_wnd;
|
||||
|
||||
/* send data1 */
|
||||
err = tcp_write(pcb, data1, sizeof(data1), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
EXPECT_RET(txcounters.num_tx_calls == 1);
|
||||
EXPECT_RET(txcounters.num_tx_bytes == sizeof(data1) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
/* "recv" ACK for data1 */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 4, TCP_ACK);
|
||||
EXPECT_RET(p != NULL);
|
||||
test_tcp_input(p, &netif);
|
||||
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||
EXPECT_RET(pcb->unacked == NULL);
|
||||
/* send data2 */
|
||||
err = tcp_write(pcb, data2, sizeof(data2), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
EXPECT_RET(txcounters.num_tx_calls == 1);
|
||||
EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
/* duplicate ACK for data1 (data2 is lost) */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||
EXPECT_RET(p != NULL);
|
||||
test_tcp_input(p, &netif);
|
||||
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||
EXPECT_RET(pcb->dupacks == 1);
|
||||
/* send data3 */
|
||||
err = tcp_write(pcb, data3, sizeof(data3), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/* nagle enabled, no tx calls */
|
||||
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||
EXPECT_RET(txcounters.num_tx_bytes == 0);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
/* 2nd duplicate ACK for data1 (data2 and data3 are lost) */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||
EXPECT_RET(p != NULL);
|
||||
test_tcp_input(p, &netif);
|
||||
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||
EXPECT_RET(pcb->dupacks == 2);
|
||||
/* queue data4, don't send it (unsent-oversize is != 0) */
|
||||
err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/* 3nd duplicate ACK for data1 (data2 and data3 are lost) -> fast retransmission */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||
EXPECT_RET(p != NULL);
|
||||
test_tcp_input(p, &netif);
|
||||
/*EXPECT_RET(txcounters.num_tx_calls == 1);*/
|
||||
EXPECT_RET(pcb->dupacks == 3);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
/* @todo: check expected data?*/
|
||||
|
||||
/* send data5, not output yet */
|
||||
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/*err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);*/
|
||||
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||
EXPECT_RET(txcounters.num_tx_bytes == 0);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
{
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
err = tcp_write(pcb, data6, TCP_MSS, TCP_WRITE_FLAG_COPY);
|
||||
i++;
|
||||
}while(err == ERR_OK);
|
||||
EXPECT_RET(err != ERR_OK);
|
||||
}
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/*EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||
EXPECT_RET(txcounters.num_tx_bytes == 0);*/
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
|
||||
/* send even more data */
|
||||
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/* ...and even more data */
|
||||
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/* ...and even more data */
|
||||
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/* ...and even more data */
|
||||
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
|
||||
/* send ACKs for data2 and data3 */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 12, TCP_ACK);
|
||||
EXPECT_RET(p != NULL);
|
||||
test_tcp_input(p, &netif);
|
||||
/*EXPECT_RET(txcounters.num_tx_calls == 0);*/
|
||||
|
||||
/* ...and even more data */
|
||||
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
/* ...and even more data */
|
||||
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
|
||||
#if 0
|
||||
/* create expected segment */
|
||||
p1 = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
|
||||
EXPECT_RET(p != NULL);
|
||||
if (p != NULL) {
|
||||
/* pass the segment to tcp_input */
|
||||
test_tcp_input(p, &netif);
|
||||
/* check if counters are as expected */
|
||||
EXPECT_RET(counters.close_calls == 0);
|
||||
EXPECT_RET(counters.recv_calls == 1);
|
||||
EXPECT_RET(counters.recved_bytes == data_len);
|
||||
EXPECT_RET(counters.err_calls == 0);
|
||||
}
|
||||
#endif
|
||||
/* make sure the pcb is freed */
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
|
||||
tcp_abort(pcb);
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static u8_t tx_data[TCP_WND*2];
|
||||
|
||||
static void
|
||||
check_seqnos(struct tcp_seg *segs, int num_expected, u32_t *seqnos_expected)
|
||||
{
|
||||
struct tcp_seg *s = segs;
|
||||
int i;
|
||||
for (i = 0; i < num_expected; i++, s = s->next) {
|
||||
EXPECT_RET(s != NULL);
|
||||
EXPECT(s->tcphdr->seqno == htonl(seqnos_expected[i]));
|
||||
}
|
||||
EXPECT(s == NULL);
|
||||
}
|
||||
|
||||
/** Send data with sequence numbers that wrap around the u32_t range.
|
||||
* Then, provoke fast retransmission by duplicate ACKs and check that all
|
||||
* segment lists are still properly sorted. */
|
||||
START_TEST(test_tcp_fast_rexmit_wraparound)
|
||||
{
|
||||
struct netif netif;
|
||||
struct test_tcp_txcounters txcounters;
|
||||
struct test_tcp_counters counters;
|
||||
struct tcp_pcb* pcb;
|
||||
struct pbuf* p;
|
||||
ip_addr_t remote_ip, local_ip, netmask;
|
||||
u16_t remote_port = 0x100, local_port = 0x101;
|
||||
err_t err;
|
||||
#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
|
||||
#define ISS 6510
|
||||
u16_t i, sent_total = 0;
|
||||
u32_t seqnos[] = {
|
||||
SEQNO1,
|
||||
SEQNO1 + (1 * TCP_MSS),
|
||||
SEQNO1 + (2 * TCP_MSS),
|
||||
SEQNO1 + (3 * TCP_MSS),
|
||||
SEQNO1 + (4 * TCP_MSS),
|
||||
SEQNO1 + (5 * TCP_MSS)};
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
for (i = 0; i < sizeof(tx_data); i++) {
|
||||
tx_data[i] = (u8_t)i;
|
||||
}
|
||||
|
||||
/* initialize local vars */
|
||||
IP_ADDR4(&local_ip, 192, 168, 1, 1);
|
||||
IP_ADDR4(&remote_ip, 192, 168, 1, 2);
|
||||
IP_ADDR4(&netmask, 255, 255, 255, 0);
|
||||
test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
|
||||
memset(&counters, 0, sizeof(counters));
|
||||
|
||||
/* create and initialize the pcb */
|
||||
tcp_ticks = SEQNO1 - ISS;
|
||||
pcb = test_tcp_new_counters_pcb(&counters);
|
||||
EXPECT_RET(pcb != NULL);
|
||||
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
|
||||
pcb->mss = TCP_MSS;
|
||||
/* disable initial congestion window (we don't send a SYN here...) */
|
||||
pcb->cwnd = 2*TCP_MSS;
|
||||
/* start in congestion advoidance */
|
||||
pcb->ssthresh = pcb->cwnd;
|
||||
|
||||
/* send 6 mss-sized segments */
|
||||
for (i = 0; i < 6; i++) {
|
||||
err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
sent_total += TCP_MSS;
|
||||
}
|
||||
check_seqnos(pcb->unsent, 6, seqnos);
|
||||
EXPECT(pcb->unacked == NULL);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT(txcounters.num_tx_calls == 2);
|
||||
EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
|
||||
check_seqnos(pcb->unacked, 2, seqnos);
|
||||
check_seqnos(pcb->unsent, 4, &seqnos[2]);
|
||||
|
||||
/* ACK the first segment */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK);
|
||||
test_tcp_input(p, &netif);
|
||||
/* ensure this didn't trigger a retransmission. Only one
|
||||
segment should be transmitted because cwnd opened up by
|
||||
TCP_MSS and a fraction since we are in congestion avoidance */
|
||||
EXPECT(txcounters.num_tx_calls == 1);
|
||||
EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
check_seqnos(pcb->unacked, 2, &seqnos[1]);
|
||||
check_seqnos(pcb->unsent, 3, &seqnos[3]);
|
||||
|
||||
/* 3 dupacks */
|
||||
EXPECT(pcb->dupacks == 0);
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||
test_tcp_input(p, &netif);
|
||||
EXPECT(txcounters.num_tx_calls == 0);
|
||||
EXPECT(pcb->dupacks == 1);
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||
test_tcp_input(p, &netif);
|
||||
EXPECT(txcounters.num_tx_calls == 0);
|
||||
EXPECT(pcb->dupacks == 2);
|
||||
/* 3rd dupack -> fast rexmit */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||
test_tcp_input(p, &netif);
|
||||
EXPECT(pcb->dupacks == 3);
|
||||
EXPECT(txcounters.num_tx_calls == 4);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
EXPECT(pcb->unsent == NULL);
|
||||
check_seqnos(pcb->unacked, 5, &seqnos[1]);
|
||||
|
||||
/* make sure the pcb is freed */
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
|
||||
tcp_abort(pcb);
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Send data with sequence numbers that wrap around the u32_t range.
|
||||
* Then, provoke RTO retransmission and check that all
|
||||
* segment lists are still properly sorted. */
|
||||
START_TEST(test_tcp_rto_rexmit_wraparound)
|
||||
{
|
||||
struct netif netif;
|
||||
struct test_tcp_txcounters txcounters;
|
||||
struct test_tcp_counters counters;
|
||||
struct tcp_pcb* pcb;
|
||||
ip_addr_t remote_ip, local_ip, netmask;
|
||||
u16_t remote_port = 0x100, local_port = 0x101;
|
||||
err_t err;
|
||||
#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
|
||||
#define ISS 6510
|
||||
u16_t i, sent_total = 0;
|
||||
u32_t seqnos[] = {
|
||||
SEQNO1,
|
||||
SEQNO1 + (1 * TCP_MSS),
|
||||
SEQNO1 + (2 * TCP_MSS),
|
||||
SEQNO1 + (3 * TCP_MSS),
|
||||
SEQNO1 + (4 * TCP_MSS),
|
||||
SEQNO1 + (5 * TCP_MSS)};
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
for (i = 0; i < sizeof(tx_data); i++) {
|
||||
tx_data[i] = (u8_t)i;
|
||||
}
|
||||
|
||||
/* initialize local vars */
|
||||
IP_ADDR4(&local_ip, 192, 168, 1, 1);
|
||||
IP_ADDR4(&remote_ip, 192, 168, 1, 2);
|
||||
IP_ADDR4(&netmask, 255, 255, 255, 0);
|
||||
test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
|
||||
memset(&counters, 0, sizeof(counters));
|
||||
|
||||
/* create and initialize the pcb */
|
||||
tcp_ticks = 0;
|
||||
tcp_ticks = 0 - tcp_next_iss(NULL);
|
||||
tcp_ticks = SEQNO1 - tcp_next_iss(NULL);
|
||||
pcb = test_tcp_new_counters_pcb(&counters);
|
||||
EXPECT_RET(pcb != NULL);
|
||||
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
|
||||
pcb->mss = TCP_MSS;
|
||||
/* disable initial congestion window (we don't send a SYN here...) */
|
||||
pcb->cwnd = 2*TCP_MSS;
|
||||
|
||||
/* send 6 mss-sized segments */
|
||||
for (i = 0; i < 6; i++) {
|
||||
err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
sent_total += TCP_MSS;
|
||||
}
|
||||
check_seqnos(pcb->unsent, 6, seqnos);
|
||||
EXPECT(pcb->unacked == NULL);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT(txcounters.num_tx_calls == 2);
|
||||
EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
|
||||
check_seqnos(pcb->unacked, 2, seqnos);
|
||||
check_seqnos(pcb->unsent, 4, &seqnos[2]);
|
||||
|
||||
/* call the tcp timer some times */
|
||||
for (i = 0; i < 10; i++) {
|
||||
test_tcp_tmr();
|
||||
EXPECT(txcounters.num_tx_calls == 0);
|
||||
}
|
||||
/* 11th call to tcp_tmr: RTO rexmit fires */
|
||||
test_tcp_tmr();
|
||||
EXPECT(txcounters.num_tx_calls == 1);
|
||||
check_seqnos(pcb->unacked, 1, seqnos);
|
||||
check_seqnos(pcb->unsent, 5, &seqnos[1]);
|
||||
|
||||
/* fake greater cwnd */
|
||||
pcb->cwnd = pcb->snd_wnd;
|
||||
/* send more data */
|
||||
err = tcp_output(pcb);
|
||||
EXPECT(err == ERR_OK);
|
||||
/* check queues are sorted */
|
||||
EXPECT(pcb->unsent == NULL);
|
||||
check_seqnos(pcb->unacked, 6, seqnos);
|
||||
|
||||
/* make sure the pcb is freed */
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
|
||||
tcp_abort(pcb);
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
|
||||
* At the end, send more data. */
|
||||
static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)
|
||||
{
|
||||
struct netif netif;
|
||||
struct test_tcp_txcounters txcounters;
|
||||
struct test_tcp_counters counters;
|
||||
struct tcp_pcb* pcb;
|
||||
struct pbuf *p;
|
||||
ip_addr_t remote_ip, local_ip, netmask;
|
||||
u16_t remote_port = 0x100, local_port = 0x101;
|
||||
err_t err;
|
||||
u16_t sent_total, i;
|
||||
u8_t expected = 0xFE;
|
||||
|
||||
for (i = 0; i < sizeof(tx_data); i++) {
|
||||
u8_t d = (u8_t)i;
|
||||
if (d == 0xFE) {
|
||||
d = 0xF0;
|
||||
}
|
||||
tx_data[i] = d;
|
||||
}
|
||||
if (zero_window_probe_from_unsent) {
|
||||
tx_data[TCP_WND] = expected;
|
||||
} else {
|
||||
tx_data[0] = expected;
|
||||
}
|
||||
|
||||
/* initialize local vars */
|
||||
IP_ADDR4(&local_ip, 192, 168, 1, 1);
|
||||
IP_ADDR4(&remote_ip, 192, 168, 1, 2);
|
||||
IP_ADDR4(&netmask, 255, 255, 255, 0);
|
||||
test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
|
||||
memset(&counters, 0, sizeof(counters));
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
|
||||
/* create and initialize the pcb */
|
||||
pcb = test_tcp_new_counters_pcb(&counters);
|
||||
EXPECT_RET(pcb != NULL);
|
||||
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
|
||||
pcb->mss = TCP_MSS;
|
||||
/* disable initial congestion window (we don't send a SYN here...) */
|
||||
pcb->cwnd = pcb->snd_wnd;
|
||||
|
||||
/* send a full window (minus 1 packets) of TCP data in MSS-sized chunks */
|
||||
sent_total = 0;
|
||||
if ((TCP_WND - TCP_MSS) % TCP_MSS != 0) {
|
||||
u16_t initial_data_len = (TCP_WND - TCP_MSS) % TCP_MSS;
|
||||
err = tcp_write(pcb, &tx_data[sent_total], initial_data_len, TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
EXPECT(txcounters.num_tx_calls == 1);
|
||||
EXPECT(txcounters.num_tx_bytes == initial_data_len + 40U);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
sent_total += initial_data_len;
|
||||
}
|
||||
for (; sent_total < (TCP_WND - TCP_MSS); sent_total += TCP_MSS) {
|
||||
err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
EXPECT(txcounters.num_tx_calls == 1);
|
||||
EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
}
|
||||
EXPECT(sent_total == (TCP_WND - TCP_MSS));
|
||||
|
||||
/* now ACK the packet before the first */
|
||||
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||
test_tcp_input(p, &netif);
|
||||
/* ensure this didn't trigger a retransmission */
|
||||
EXPECT(txcounters.num_tx_calls == 0);
|
||||
EXPECT(txcounters.num_tx_bytes == 0);
|
||||
|
||||
EXPECT(pcb->persist_backoff == 0);
|
||||
/* send the last packet, now a complete window has been sent */
|
||||
err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
|
||||
sent_total += TCP_MSS;
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
EXPECT(txcounters.num_tx_calls == 1);
|
||||
EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
EXPECT(pcb->persist_backoff == 0);
|
||||
|
||||
if (zero_window_probe_from_unsent) {
|
||||
/* ACK all data but close the TX window */
|
||||
p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_WND, TCP_ACK, 0);
|
||||
test_tcp_input(p, &netif);
|
||||
/* ensure this didn't trigger any transmission */
|
||||
EXPECT(txcounters.num_tx_calls == 0);
|
||||
EXPECT(txcounters.num_tx_bytes == 0);
|
||||
EXPECT(pcb->persist_backoff == 1);
|
||||
}
|
||||
|
||||
/* send one byte more (out of window) -> persist timer starts */
|
||||
err = tcp_write(pcb, &tx_data[sent_total], 1, TCP_WRITE_FLAG_COPY);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
err = tcp_output(pcb);
|
||||
EXPECT_RET(err == ERR_OK);
|
||||
EXPECT(txcounters.num_tx_calls == 0);
|
||||
EXPECT(txcounters.num_tx_bytes == 0);
|
||||
memset(&txcounters, 0, sizeof(txcounters));
|
||||
if (!zero_window_probe_from_unsent) {
|
||||
/* no persist timer unless a zero window announcement has been received */
|
||||
EXPECT(pcb->persist_backoff == 0);
|
||||
} else {
|
||||
EXPECT(pcb->persist_backoff == 1);
|
||||
|
||||
/* call tcp_timer some more times to let persist timer count up */
|
||||
for (i = 0; i < 4; i++) {
|
||||
test_tcp_tmr();
|
||||
EXPECT(txcounters.num_tx_calls == 0);
|
||||
EXPECT(txcounters.num_tx_bytes == 0);
|
||||
}
|
||||
|
||||
/* this should trigger the zero-window-probe */
|
||||
txcounters.copy_tx_packets = 1;
|
||||
test_tcp_tmr();
|
||||
txcounters.copy_tx_packets = 0;
|
||||
EXPECT(txcounters.num_tx_calls == 1);
|
||||
EXPECT(txcounters.num_tx_bytes == 1 + 40U);
|
||||
EXPECT(txcounters.tx_packets != NULL);
|
||||
if (txcounters.tx_packets != NULL) {
|
||||
u8_t sent;
|
||||
u16_t ret;
|
||||
ret = pbuf_copy_partial(txcounters.tx_packets, &sent, 1, 40U);
|
||||
EXPECT(ret == 1);
|
||||
EXPECT(sent == expected);
|
||||
}
|
||||
if (txcounters.tx_packets != NULL) {
|
||||
pbuf_free(txcounters.tx_packets);
|
||||
txcounters.tx_packets = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure the pcb is freed */
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
|
||||
tcp_abort(pcb);
|
||||
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
||||
}
|
||||
|
||||
START_TEST(test_tcp_tx_full_window_lost_from_unsent)
|
||||
{
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
test_tcp_tx_full_window_lost(1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_tcp_tx_full_window_lost_from_unacked)
|
||||
{
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
test_tcp_tx_full_window_lost(0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
tcp_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_tcp_new_abort),
|
||||
TESTFUNC(test_tcp_recv_inseq),
|
||||
TESTFUNC(test_tcp_malformed_header),
|
||||
TESTFUNC(test_tcp_fast_retx_recover),
|
||||
TESTFUNC(test_tcp_fast_rexmit_wraparound),
|
||||
TESTFUNC(test_tcp_rto_rexmit_wraparound),
|
||||
TESTFUNC(test_tcp_tx_full_window_lost_from_unacked),
|
||||
TESTFUNC(test_tcp_tx_full_window_lost_from_unsent)
|
||||
};
|
||||
return create_suite("TCP", tests, sizeof(tests)/sizeof(testfunc), tcp_setup, tcp_teardown);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_TCP_H
|
||||
#define LWIP_HDR_TEST_TCP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *tcp_suite(void);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_TCP_OOS_H
|
||||
#define LWIP_HDR_TEST_TCP_OOS_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *tcp_oos_suite(void);
|
||||
|
||||
#endif
|
||||
@@ -1,68 +0,0 @@
|
||||
#include "test_udp.h"
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS
|
||||
#error "This tests needs UDP- and MEMP-statistics enabled"
|
||||
#endif
|
||||
|
||||
/* Helper functions */
|
||||
static void
|
||||
udp_remove_all(void)
|
||||
{
|
||||
struct udp_pcb *pcb = udp_pcbs;
|
||||
struct udp_pcb *pcb2;
|
||||
|
||||
while(pcb != NULL) {
|
||||
pcb2 = pcb;
|
||||
pcb = pcb->next;
|
||||
udp_remove(pcb2);
|
||||
}
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
|
||||
}
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
udp_setup(void)
|
||||
{
|
||||
udp_remove_all();
|
||||
}
|
||||
|
||||
static void
|
||||
udp_teardown(void)
|
||||
{
|
||||
udp_remove_all();
|
||||
}
|
||||
|
||||
|
||||
/* Test functions */
|
||||
|
||||
START_TEST(test_udp_new_remove)
|
||||
{
|
||||
struct udp_pcb* pcb;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
|
||||
|
||||
pcb = udp_new();
|
||||
fail_unless(pcb != NULL);
|
||||
if (pcb != NULL) {
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 1);
|
||||
udp_remove(pcb);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
udp_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_udp_new_remove),
|
||||
};
|
||||
return create_suite("UDP", tests, sizeof(tests)/sizeof(testfunc), udp_setup, udp_teardown);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef LWIP_HDR_TEST_UDP_H
|
||||
#define LWIP_HDR_TEST_UDP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* udp_suite(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user