IPv6 working, just barely
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -10,28 +10,38 @@ 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) including packet forwarding over multiple network
|
||||
interfaces
|
||||
* 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
|
||||
* Specialized raw/native API for enhanced performance
|
||||
* raw/native API for enhanced performance
|
||||
* Optional Berkeley-like socket API
|
||||
* DNS (Domain names resolver)
|
||||
* SNMP (Simple Network Management Protocol)
|
||||
* DHCP (Dynamic Host Configuration Protocol)
|
||||
* AUTOIP (for IPv4, conform with RFC 3927)
|
||||
* PPP (Point-to-Point Protocol)
|
||||
* ARP (Address Resolution Protocol) for Ethernet
|
||||
|
||||
|
||||
APPLICATIONS
|
||||
|
||||
* HTTP server with SSI and CGI
|
||||
* SNMPv2c agent with MIB compiler (Simple Network Management Protocol)
|
||||
* SNTP (Simple network time protocol)
|
||||
|
||||
|
||||
LICENSE
|
||||
|
||||
lwIP is freely available under a BSD license.
|
||||
|
||||
|
||||
DEVELOPMENT
|
||||
|
||||
lwIP has grown into an excellent TCP/IP stack for embedded devices,
|
||||
@@ -40,35 +50,31 @@ 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, CVS and the
|
||||
help improve lwIP by use of Savannah's interface, Git and the
|
||||
mailing list. A core team of developers will commit changes to the
|
||||
CVS source tree.
|
||||
Git source tree.
|
||||
|
||||
The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and
|
||||
contributions (such as platform ports) are in the 'contrib' module.
|
||||
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 CVS server access for users and
|
||||
See doc/savannah.txt for details on Git server access for users and
|
||||
developers.
|
||||
|
||||
Last night's CVS tar ball can be downloaded from:
|
||||
http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING]
|
||||
|
||||
The current CVS trees are web-browsable:
|
||||
http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/
|
||||
http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/
|
||||
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
|
||||
|
||||
The original out-dated homepage of lwIP and Adam Dunkels' papers on
|
||||
lwIP are at the official lwIP home page:
|
||||
http://www.sics.se/~adam/lwip/
|
||||
|
||||
Self documentation of the source code is regularly extracted from the
|
||||
current CVS sources and is available from this web page:
|
||||
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 growin wiki about lwIP at
|
||||
@@ -80,10 +86,12 @@ 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>
|
||||
|
||||
@@ -4,10 +4,83 @@ application written for an older version of lwIP to correctly work
|
||||
with newer versions.
|
||||
|
||||
|
||||
(CVS HEAD)
|
||||
(git master)
|
||||
|
||||
* [Enter new changes just after this line - do not remove this line]
|
||||
|
||||
* TODO
|
||||
|
||||
(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 SNMP2vc (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!
|
||||
* 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)
|
||||
|
||||
+++ 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
|
||||
|
||||
* 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)
|
||||
* 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 reorderd stats (mainly memp/mib2)
|
||||
|
||||
(1.4.0)
|
||||
|
||||
++ Application changes:
|
||||
|
||||
* Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
|
||||
6
ext/lwip/doc/FILES
Normal file
6
ext/lwip/doc/FILES
Normal file
@@ -0,0 +1,6 @@
|
||||
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.
|
||||
snmp_agent.txt - The documentation for the lwIP SNMP agent.
|
||||
sys_arch.txt - The documentation for a system abstraction layer of lwIP.
|
||||
58
ext/lwip/doc/contrib.txt
Normal file
58
ext/lwip/doc/contrib.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
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 prefered way
|
||||
is to NOT touch archs you can't test and let maintainers take care of them.
|
||||
This is a good way to see if they are used at all - the same goes for unix
|
||||
netifs except tapif.
|
||||
4. Do not file a bug and post a fix to it to the patch area. Either a bug report
|
||||
or a patch will be enough.
|
||||
If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
|
||||
5. 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
ext/lwip/doc/doxygen/generate.bat
Normal file
1
ext/lwip/doc/doxygen/generate.bat
Normal file
@@ -0,0 +1 @@
|
||||
doxygen lwip.Doxyfile
|
||||
1
ext/lwip/doc/doxygen/generate.sh
Executable file
1
ext/lwip/doc/doxygen/generate.sh
Executable file
@@ -0,0 +1 @@
|
||||
doxygen lwip.Doxyfile
|
||||
32
ext/lwip/doc/doxygen/main_page.h
Normal file
32
ext/lwip/doc/doxygen/main_page.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @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 threadsafe_api Thread-safe APIs
|
||||
* Thread-safe 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 contrib How to contribute to lwIP
|
||||
* @verbinclude "contrib.txt"
|
||||
*/
|
||||
529
ext/lwip/doc/ppp.txt
Normal file
529
ext/lwip/doc/ppp.txt
Normal file
@@ -0,0 +1,529 @@
|
||||
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
|
||||
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
|
||||
506
ext/lwip/doc/rawapi.txt
Normal file
506
ext/lwip/doc/rawapi.txt
Normal file
@@ -0,0 +1,506 @@
|
||||
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_accepted(struct tcp_pcb *pcb)
|
||||
|
||||
Inform lwIP that an incoming connection has been accepted. This would
|
||||
usually be called from the accept callback. This allows lwIP to perform
|
||||
housekeeping tasks, such as allowing further incoming connections to be
|
||||
queued in the listen backlog.
|
||||
ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed
|
||||
into the accept callback!
|
||||
|
||||
- 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_PLATFORM_BYTESWAP 1
|
||||
#define LWIP_PLATFORM_HTONS(x) <your_htons>
|
||||
#define LWIP_PLATFORM_HTONL(x) <your_htonl>
|
||||
|
||||
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!
|
||||
120
ext/lwip/doc/savannah.txt
Normal file
120
ext/lwip/doc/savannah.txt
Normal file
@@ -0,0 +1,120 @@
|
||||
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.
|
||||
267
ext/lwip/doc/sys_arch.txt
Normal file
267
ext/lwip/doc/sys_arch.txt
Normal file
@@ -0,0 +1,267 @@
|
||||
sys_arch interface for lwIP 0.6++
|
||||
|
||||
Author: Adam Dunkels
|
||||
|
||||
The operating system emulation layer provides a common interface
|
||||
between the lwIP code and the underlying operating system kernel. The
|
||||
general idea is that porting lwIP to new architectures requires only
|
||||
small changes to a few header files and a new sys_arch
|
||||
implementation. It is also possible to do a sys_arch implementation
|
||||
that does not rely on any underlying operating system.
|
||||
|
||||
The sys_arch provides semaphores and mailboxes to lwIP. For the full
|
||||
lwIP functionality, multiple threads support can be implemented in the
|
||||
sys_arch, but this is not required for the basic lwIP
|
||||
functionality. Previous versions of lwIP required the sys_arch to
|
||||
implement timer scheduling as well but as of lwIP 0.5 this is
|
||||
implemented in a higher layer.
|
||||
|
||||
In addition to the source file providing the functionality of sys_arch,
|
||||
the OS emulation layer must provide several header files defining
|
||||
macros used throughout lwip. The files required and the macros they
|
||||
must define are listed below the sys_arch description.
|
||||
|
||||
Semaphores can be either counting or binary - lwIP works with both
|
||||
kinds. Mailboxes are used for message passing and can be implemented
|
||||
either as a queue which allows multiple messages to be posted to a
|
||||
mailbox, or as a rendez-vous point where only one message can be
|
||||
posted at a time. lwIP works with both kinds, but the former type will
|
||||
be more efficient. A message in a mailbox is just a pointer, nothing
|
||||
more.
|
||||
|
||||
Semaphores are represented by the type "sys_sem_t" which is typedef'd
|
||||
in the sys_arch.h file. Mailboxes are equivalently represented by the
|
||||
type "sys_mbox_t". lwIP does not place any restrictions on how
|
||||
sys_sem_t or sys_mbox_t are represented internally.
|
||||
|
||||
Since lwIP 1.4.0, semaphore 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.
|
||||
|
||||
- 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.
|
||||
|
||||
- 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 carefull with using mem_malloc() in sys_arch. When malloc() refers to
|
||||
mem_malloc() you can run into a circular function call problem. In mem.c
|
||||
mem_init() tries to allcate a semaphore using mem_malloc, which of course
|
||||
can't be performed when sys_arch uses mem_malloc.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Additional files required for the "OS support" emulation layer:
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
cc.h - Architecture environment, some compiler specific, some
|
||||
environment specific (probably should move env stuff
|
||||
to sys_arch.h.)
|
||||
|
||||
Typedefs for the types used by lwip -
|
||||
u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t
|
||||
|
||||
Compiler hints for packing lwip's structures -
|
||||
PACK_STRUCT_FIELD(x)
|
||||
PACK_STRUCT_STRUCT
|
||||
PACK_STRUCT_BEGIN
|
||||
PACK_STRUCT_END
|
||||
|
||||
Platform specific diagnostic output -
|
||||
LWIP_PLATFORM_DIAG(x) - non-fatal, print a message.
|
||||
LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution.
|
||||
Portability defines for printf formatters:
|
||||
U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
|
||||
|
||||
"lightweight" synchronization mechanisms -
|
||||
SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable.
|
||||
SYS_ARCH_PROTECT(x) - enter protection mode.
|
||||
SYS_ARCH_UNPROTECT(x) - leave protection mode.
|
||||
|
||||
If the compiler does not provide memset() this file must include a
|
||||
definition of it, or include a file which defines it.
|
||||
|
||||
This file must either include a system-local <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
|
||||
169
ext/lwip/src/Filelists.mk
Normal file
169
ext/lwip/src/Filelists.mk
Normal file
@@ -0,0 +1,169 @@
|
||||
#
|
||||
# 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>
|
||||
#
|
||||
|
||||
# COREFILES, CORE4FILES: The minimum set of files needed for lwIP.
|
||||
COREFILES=$(LWIPDIR)/core/init.c \
|
||||
$(LWIPDIR)/core/def.c \
|
||||
$(LWIPDIR)/core/dns.c \
|
||||
$(LWIPDIR)/core/inet_chksum.c \
|
||||
$(LWIPDIR)/core/ip.c \
|
||||
$(LWIPDIR)/core/mem.c \
|
||||
$(LWIPDIR)/core/memp.c \
|
||||
$(LWIPDIR)/core/netif.c \
|
||||
$(LWIPDIR)/core/pbuf.c \
|
||||
$(LWIPDIR)/core/raw.c \
|
||||
$(LWIPDIR)/core/stats.c \
|
||||
$(LWIPDIR)/core/sys.c \
|
||||
$(LWIPDIR)/core/tcp.c \
|
||||
$(LWIPDIR)/core/tcp_in.c \
|
||||
$(LWIPDIR)/core/tcp_out.c \
|
||||
$(LWIPDIR)/core/timeouts.c \
|
||||
$(LWIPDIR)/core/udp.c
|
||||
|
||||
CORE4FILES=$(LWIPDIR)/core/ipv4/autoip.c \
|
||||
$(LWIPDIR)/core/ipv4/dhcp.c \
|
||||
$(LWIPDIR)/core/ipv4/etharp.c \
|
||||
$(LWIPDIR)/core/ipv4/icmp.c \
|
||||
$(LWIPDIR)/core/ipv4/igmp.c \
|
||||
$(LWIPDIR)/core/ipv4/ip4_frag.c \
|
||||
$(LWIPDIR)/core/ipv4/ip4.c \
|
||||
$(LWIPDIR)/core/ipv4/ip4_addr.c
|
||||
|
||||
CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \
|
||||
$(LWIPDIR)/core/ipv6/ethip6.c \
|
||||
$(LWIPDIR)/core/ipv6/icmp6.c \
|
||||
$(LWIPDIR)/core/ipv6/inet6.c \
|
||||
$(LWIPDIR)/core/ipv6/ip6.c \
|
||||
$(LWIPDIR)/core/ipv6/ip6_addr.c \
|
||||
$(LWIPDIR)/core/ipv6/ip6_frag.c \
|
||||
$(LWIPDIR)/core/ipv6/mld6.c \
|
||||
$(LWIPDIR)/core/ipv6/nd6.c
|
||||
|
||||
# APIFILES: The files which implement the sequential and socket APIs.
|
||||
APIFILES=$(LWIPDIR)/api/api_lib.c \
|
||||
$(LWIPDIR)/api/api_msg.c \
|
||||
$(LWIPDIR)/api/err.c \
|
||||
$(LWIPDIR)/api/netbuf.c \
|
||||
$(LWIPDIR)/api/netdb.c \
|
||||
$(LWIPDIR)/api/netifapi.c \
|
||||
$(LWIPDIR)/api/sockets.c \
|
||||
$(LWIPDIR)/api/tcpip.c
|
||||
|
||||
# NETIFFILES: Files implementing various generic network interface functions
|
||||
NETIFFILES=$(LWIPDIR)/netif/ethernet.c \
|
||||
$(LWIPDIR)/netif/slipif.c
|
||||
|
||||
# SIXLOWPAN: 6LoWPAN
|
||||
SIXLOWPAN=$(LWIPDIR)/netif/lowpan6.c \
|
||||
|
||||
# PPPFILES: PPP
|
||||
PPPFILES=$(LWIPDIR)/netif/ppp/auth.c \
|
||||
$(LWIPDIR)/netif/ppp/ccp.c \
|
||||
$(LWIPDIR)/netif/ppp/chap-md5.c \
|
||||
$(LWIPDIR)/netif/ppp/chap_ms.c \
|
||||
$(LWIPDIR)/netif/ppp/chap-new.c \
|
||||
$(LWIPDIR)/netif/ppp/demand.c \
|
||||
$(LWIPDIR)/netif/ppp/eap.c \
|
||||
$(LWIPDIR)/netif/ppp/ecp.c \
|
||||
$(LWIPDIR)/netif/ppp/eui64.c \
|
||||
$(LWIPDIR)/netif/ppp/fsm.c \
|
||||
$(LWIPDIR)/netif/ppp/ipcp.c \
|
||||
$(LWIPDIR)/netif/ppp/ipv6cp.c \
|
||||
$(LWIPDIR)/netif/ppp/lcp.c \
|
||||
$(LWIPDIR)/netif/ppp/magic.c \
|
||||
$(LWIPDIR)/netif/ppp/mppe.c \
|
||||
$(LWIPDIR)/netif/ppp/multilink.c \
|
||||
$(LWIPDIR)/netif/ppp/ppp.c \
|
||||
$(LWIPDIR)/netif/ppp/pppapi.c \
|
||||
$(LWIPDIR)/netif/ppp/pppcrypt.c \
|
||||
$(LWIPDIR)/netif/ppp/pppoe.c \
|
||||
$(LWIPDIR)/netif/ppp/pppol2tp.c \
|
||||
$(LWIPDIR)/netif/ppp/pppos.c \
|
||||
$(LWIPDIR)/netif/ppp/upap.c \
|
||||
$(LWIPDIR)/netif/ppp/utils.c \
|
||||
$(LWIPDIR)/netif/ppp/vj.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/arc4.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/des.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/md4.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/md5.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/sha1.c
|
||||
|
||||
# LWIPNOAPPSFILES: All LWIP files without apps
|
||||
LWIPNOAPPSFILES=$(COREFILES) \
|
||||
$(CORE4FILES) \
|
||||
$(CORE6FILES) \
|
||||
$(APIFILES) \
|
||||
$(NETIFFILES) \
|
||||
$(PPPFILES) \
|
||||
$(SIXLOWPAN)
|
||||
|
||||
# SNMPFILES: SNMPv2c agent
|
||||
SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_core.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_icmp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_interfaces.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_ip.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_snmp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_system.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_msg.c \
|
||||
$(LWIPDIR)/apps/snmp/snmpv3.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_netconn.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_pbuf_stream.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_raw.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_scalar.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_table.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_threadsync.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_traps.c \
|
||||
$(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c \
|
||||
$(LWIPDIR)/apps/snmp/snmpv3_dummy.c
|
||||
|
||||
# HTTPDFILES: HTTP server
|
||||
HTTPDFILES=$(LWIPDIR)/apps/httpd/fs.c \
|
||||
$(LWIPDIR)/apps/httpd/httpd.c
|
||||
|
||||
# LWIPERFFILES: IPERF server
|
||||
LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c
|
||||
|
||||
# SNTPFILES: SNTP client
|
||||
SNTPFILES=$(LWIPDIR)/apps/sntp/sntp.c
|
||||
|
||||
# NETBIOSNSFILES: NetBIOS name server
|
||||
NETBIOSNSFILES=$(LWIPDIR)/apps/netbiosns/netbiosns.c
|
||||
|
||||
# LWIPAPPFILES: All LWIP APPs
|
||||
LWIPAPPFILES=$(SNMPFILES) \
|
||||
$(HTTPDFILES) \
|
||||
$(LWIPERFFILES) \
|
||||
$(SNTPFILES) \
|
||||
$(NETBIOSNSFILES)
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file
|
||||
* Sequential API External module
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -33,7 +32,26 @@
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup netconn Netconn API
|
||||
* @ingroup threadsafe_api
|
||||
* Thread-safe, to be called from non-TCPIP threads only.
|
||||
* TX/RX handling based on @ref netbuf (containing @ref pbuf)
|
||||
* to avoid copying data around.
|
||||
*
|
||||
* @defgroup netconn_common Common functions
|
||||
* @ingroup netconn
|
||||
* For use with TCP and UDP
|
||||
*
|
||||
* @defgroup netconn_tcp TCP only
|
||||
* @ingroup netconn
|
||||
* TCP only functions
|
||||
*
|
||||
* @defgroup netconn_udp UDP only
|
||||
* @ingroup netconn
|
||||
* UDP only functions
|
||||
*/
|
||||
|
||||
/* This is the part of the API that is linked with
|
||||
@@ -44,16 +62,55 @@
|
||||
#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/memp.h"
|
||||
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/priv/api_msg.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define API_MSG_VAR_REF(name) API_VAR_REF(name)
|
||||
#define API_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct api_msg, name)
|
||||
#define API_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
|
||||
#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
|
||||
#define API_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_API_MSG, name)
|
||||
|
||||
static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
|
||||
|
||||
/**
|
||||
* Call the lower part of a netconn_* function
|
||||
* This function is then running in the thread context
|
||||
* of tcpip_thread and has exclusive access to lwIP core code.
|
||||
*
|
||||
* @param fn function to call
|
||||
* @param apimsg a struct containing the function to call and its parameters
|
||||
* @return ERR_OK if the function was called, another err_t if not
|
||||
*/
|
||||
static err_t
|
||||
netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
/* catch functions that don't set err */
|
||||
apimsg->err = ERR_VAL;
|
||||
#endif /* LWIP_DEBUG */
|
||||
|
||||
#if LWIP_NETCONN_SEM_PER_THREAD
|
||||
apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
|
||||
if (err == ERR_OK) {
|
||||
return apimsg->err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new netconn (of a specific type) that has a callback function.
|
||||
* The corresponding pcb is also created.
|
||||
@@ -68,30 +125,38 @@ struct netconn*
|
||||
netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
|
||||
{
|
||||
struct netconn *conn;
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
API_MSG_VAR_ALLOC_RETURN_NULL(msg);
|
||||
|
||||
conn = netconn_alloc(t, callback);
|
||||
if (conn != NULL) {
|
||||
msg.function = do_newconn;
|
||||
msg.msg.msg.n.proto = proto;
|
||||
msg.msg.conn = conn;
|
||||
if (TCPIP_APIMSG(&msg) != ERR_OK) {
|
||||
err_t err;
|
||||
|
||||
API_MSG_VAR_REF(msg).msg.n.proto = proto;
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
|
||||
if (err != ERR_OK) {
|
||||
LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
|
||||
LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
|
||||
LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
|
||||
#if LWIP_TCP
|
||||
LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
|
||||
#endif /* LWIP_TCP */
|
||||
#if !LWIP_NETCONN_SEM_PER_THREAD
|
||||
LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
|
||||
sys_sem_free(&conn->op_completed);
|
||||
#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
|
||||
sys_mbox_free(&conn->recvmbox);
|
||||
memp_free(MEMP_NETCONN, conn);
|
||||
API_MSG_VAR_FREE(msg);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
API_MSG_VAR_FREE(msg);
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_common
|
||||
* Close a netconn 'connection' and free its resources.
|
||||
* UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
|
||||
* after this returns.
|
||||
@@ -102,21 +167,35 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal
|
||||
err_t
|
||||
netconn_delete(struct netconn *conn)
|
||||
{
|
||||
struct api_msg msg;
|
||||
err_t err;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
|
||||
/* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
|
||||
if (conn == NULL) {
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
msg.function = do_delconn;
|
||||
msg.msg.conn = conn;
|
||||
tcpip_apimsg(&msg);
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
|
||||
/* get the time we started, which is later compared to
|
||||
sys_now() + conn->send_timeout */
|
||||
API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
|
||||
#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||
#if LWIP_TCP
|
||||
API_MSG_VAR_REF(msg).msg.sd.polls_left =
|
||||
((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
|
||||
#endif /* LWIP_TCP */
|
||||
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||
err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
if (err != ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
netconn_free(conn);
|
||||
|
||||
/* don't care for return value of do_delconn since it only calls void functions */
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -134,25 +213,32 @@ netconn_delete(struct netconn *conn)
|
||||
err_t
|
||||
netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
|
||||
LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
|
||||
|
||||
msg.function = do_getaddr;
|
||||
msg.msg.conn = conn;
|
||||
msg.msg.msg.ad.ipaddr = addr;
|
||||
msg.msg.msg.ad.port = port;
|
||||
msg.msg.msg.ad.local = local;
|
||||
err = TCPIP_APIMSG(&msg);
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
API_MSG_VAR_REF(msg).msg.ad.local = local;
|
||||
#if LWIP_MPU_COMPATIBLE
|
||||
err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
|
||||
*addr = msg->msg.ad.ipaddr;
|
||||
*port = msg->msg.ad.port;
|
||||
#else /* LWIP_MPU_COMPATIBLE */
|
||||
msg.msg.ad.ipaddr = addr;
|
||||
msg.msg.ad.port = port;
|
||||
err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
|
||||
#endif /* LWIP_MPU_COMPATIBLE */
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_common
|
||||
* Bind a netconn to a specific local IP address and port.
|
||||
* Binding one netconn twice might not always be checked correctly!
|
||||
*
|
||||
@@ -163,24 +249,30 @@ netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
|
||||
* @return ERR_OK if bound, any other err_t on failure
|
||||
*/
|
||||
err_t
|
||||
netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
|
||||
netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
|
||||
LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
|
||||
msg.function = do_bind;
|
||||
msg.msg.conn = conn;
|
||||
msg.msg.msg.bc.ipaddr = addr;
|
||||
msg.msg.msg.bc.port = port;
|
||||
err = TCPIP_APIMSG(&msg);
|
||||
/* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
|
||||
if (addr == NULL) {
|
||||
addr = IP_ADDR_ANY;
|
||||
}
|
||||
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
|
||||
API_MSG_VAR_REF(msg).msg.bc.port = port;
|
||||
err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_common
|
||||
* Connect a netconn to a specific remote IP address and port.
|
||||
*
|
||||
* @param conn the netconn to connect
|
||||
@@ -189,47 +281,53 @@ netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
|
||||
* @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
|
||||
*/
|
||||
err_t
|
||||
netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port)
|
||||
netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
|
||||
LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
|
||||
msg.function = do_connect;
|
||||
msg.msg.conn = conn;
|
||||
msg.msg.msg.bc.ipaddr = addr;
|
||||
msg.msg.msg.bc.port = port;
|
||||
/* This is the only function which need to not block tcpip_thread */
|
||||
err = tcpip_apimsg(&msg);
|
||||
/* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
|
||||
if (addr == NULL) {
|
||||
addr = IP_ADDR_ANY;
|
||||
}
|
||||
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
|
||||
API_MSG_VAR_REF(msg).msg.bc.port = port;
|
||||
err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_udp
|
||||
* Disconnect a netconn from its current peer (only valid for UDP netconns).
|
||||
*
|
||||
* @param conn the netconn to disconnect
|
||||
* @return TODO: return value is not set here...
|
||||
* @return @todo: return value is not set here...
|
||||
*/
|
||||
err_t
|
||||
netconn_disconnect(struct netconn *conn)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
|
||||
LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
|
||||
msg.function = do_disconnect;
|
||||
msg.msg.conn = conn;
|
||||
err = TCPIP_APIMSG(&msg);
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_tcp
|
||||
* Set a TCP netconn into listen mode
|
||||
*
|
||||
* @param conn the tcp netconn to set to listen mode
|
||||
@@ -241,7 +339,7 @@ err_t
|
||||
netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
|
||||
{
|
||||
#if LWIP_TCP
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
|
||||
/* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
|
||||
@@ -249,14 +347,14 @@ netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
|
||||
|
||||
LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
|
||||
msg.function = do_listen;
|
||||
msg.msg.conn = conn;
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
msg.msg.msg.lb.backlog = backlog;
|
||||
API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
|
||||
#endif /* TCP_LISTEN_BACKLOG */
|
||||
err = TCPIP_APIMSG(&msg);
|
||||
err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
#else /* LWIP_TCP */
|
||||
LWIP_UNUSED_ARG(conn);
|
||||
@@ -266,6 +364,7 @@ netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_tcp
|
||||
* Accept a new connection on a TCP listening netconn.
|
||||
*
|
||||
* @param conn the TCP listen netconn
|
||||
@@ -277,16 +376,16 @@ err_t
|
||||
netconn_accept(struct netconn *conn, struct netconn **new_conn)
|
||||
{
|
||||
#if LWIP_TCP
|
||||
void *accept_ptr;
|
||||
struct netconn *newconn;
|
||||
err_t err;
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
#endif /* TCP_LISTEN_BACKLOG */
|
||||
|
||||
LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;);
|
||||
*new_conn = NULL;
|
||||
LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;);
|
||||
|
||||
err = conn->last_err;
|
||||
if (ERR_IS_FATAL(err)) {
|
||||
@@ -294,29 +393,53 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
|
||||
waiting on acceptmbox forever! */
|
||||
return err;
|
||||
}
|
||||
if (!sys_mbox_valid(&conn->acceptmbox)) {
|
||||
return ERR_CLSD;
|
||||
}
|
||||
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
#endif /* TCP_LISTEN_BACKLOG */
|
||||
|
||||
#if LWIP_SO_RCVTIMEO
|
||||
if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
|
||||
NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
|
||||
if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
API_MSG_VAR_FREE(msg);
|
||||
#endif /* TCP_LISTEN_BACKLOG */
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
#else
|
||||
sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0);
|
||||
sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
|
||||
#endif /* LWIP_SO_RCVTIMEO*/
|
||||
newconn = (struct netconn *)accept_ptr;
|
||||
/* Register event with callback */
|
||||
API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
|
||||
|
||||
if (accept_ptr == &netconn_aborted) {
|
||||
/* a connection has been aborted: out of pcbs or out of netconns during accept */
|
||||
/* @todo: set netconn error, but this would be fatal and thus block further accepts */
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
API_MSG_VAR_FREE(msg);
|
||||
#endif /* TCP_LISTEN_BACKLOG */
|
||||
return ERR_ABRT;
|
||||
}
|
||||
if (newconn == NULL) {
|
||||
/* connection has been aborted */
|
||||
NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
|
||||
return ERR_ABRT;
|
||||
/* in this special case, we set the netconn error from application thread, as
|
||||
on a ready-to-accept listening netconn, there should not be anything running
|
||||
in tcpip_thread */
|
||||
NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
API_MSG_VAR_FREE(msg);
|
||||
#endif /* TCP_LISTEN_BACKLOG */
|
||||
return ERR_CLSD;
|
||||
}
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
/* Let the stack know that we have accepted the connection. */
|
||||
msg.function = do_recv;
|
||||
msg.msg.conn = conn;
|
||||
/* don't care for the return value of do_recv */
|
||||
TCPIP_APIMSG(&msg);
|
||||
API_MSG_VAR_REF(msg).conn = newconn;
|
||||
/* don't care for the return value of lwip_netconn_do_recv */
|
||||
netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
#endif /* TCP_LISTEN_BACKLOG */
|
||||
|
||||
*new_conn = newconn;
|
||||
@@ -330,6 +453,7 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_common
|
||||
* Receive data: actual implementation that doesn't care whether pbuf or netbuf
|
||||
* is received
|
||||
*
|
||||
@@ -345,13 +469,27 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
|
||||
u16_t len;
|
||||
err_t err;
|
||||
#if LWIP_TCP
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
#if LWIP_MPU_COMPATIBLE
|
||||
msg = NULL;
|
||||
#endif
|
||||
#endif /* LWIP_TCP */
|
||||
|
||||
LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
|
||||
*new_buf = NULL;
|
||||
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
|
||||
#if LWIP_TCP
|
||||
#if (LWIP_UDP || LWIP_RAW)
|
||||
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
|
||||
#endif /* (LWIP_UDP || LWIP_RAW) */
|
||||
{
|
||||
if (!sys_mbox_valid(&conn->recvmbox)) {
|
||||
/* This happens when calling this function after receiving FIN */
|
||||
return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_TCP */
|
||||
LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
|
||||
|
||||
err = conn->last_err;
|
||||
if (ERR_IS_FATAL(err)) {
|
||||
@@ -361,10 +499,25 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
|
||||
before the fatal error occurred - is that a problem? */
|
||||
return err;
|
||||
}
|
||||
#if LWIP_TCP
|
||||
#if (LWIP_UDP || LWIP_RAW)
|
||||
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
|
||||
#endif /* (LWIP_UDP || LWIP_RAW) */
|
||||
{
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
}
|
||||
#endif /* LWIP_TCP */
|
||||
|
||||
#if LWIP_SO_RCVTIMEO
|
||||
if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
|
||||
NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
|
||||
#if LWIP_TCP
|
||||
#if (LWIP_UDP || LWIP_RAW)
|
||||
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
|
||||
#endif /* (LWIP_UDP || LWIP_RAW) */
|
||||
{
|
||||
API_MSG_VAR_FREE(msg);
|
||||
}
|
||||
#endif /* LWIP_TCP */
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
#else
|
||||
@@ -373,29 +526,29 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
|
||||
|
||||
#if LWIP_TCP
|
||||
#if (LWIP_UDP || LWIP_RAW)
|
||||
if (conn->type == NETCONN_TCP)
|
||||
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
|
||||
#endif /* (LWIP_UDP || LWIP_RAW) */
|
||||
{
|
||||
if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
|
||||
/* Let the stack know that we have taken the data. */
|
||||
/* TODO: Speedup: Don't block and wait for the answer here
|
||||
/* @todo: Speedup: Don't block and wait for the answer here
|
||||
(to prevent multiple thread-switches). */
|
||||
msg.function = do_recv;
|
||||
msg.msg.conn = conn;
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
if (buf != NULL) {
|
||||
msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
|
||||
API_MSG_VAR_REF(msg).msg.r.len = ((struct pbuf *)buf)->tot_len;
|
||||
} else {
|
||||
msg.msg.msg.r.len = 1;
|
||||
}
|
||||
/* don't care for the return value of do_recv */
|
||||
TCPIP_APIMSG(&msg);
|
||||
API_MSG_VAR_REF(msg).msg.r.len = 1;
|
||||
}
|
||||
|
||||
/* don't care for the return value of lwip_netconn_do_recv */
|
||||
netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
/* If we are closed, we indicate that we no longer wish to use the socket */
|
||||
if (buf == NULL) {
|
||||
API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
|
||||
/* Avoid to lose any previous error code */
|
||||
NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
|
||||
/* RX side is closed, so deallocate the recvmbox */
|
||||
netconn_close_shutdown(conn, NETCONN_SHUT_RD);
|
||||
/* Don' store ERR_CLSD as conn->err since we are only half-closed */
|
||||
return ERR_CLSD;
|
||||
}
|
||||
len = ((struct pbuf *)buf)->tot_len;
|
||||
@@ -425,6 +578,7 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_tcp
|
||||
* Receive data (in form of a pbuf) from a TCP netconn
|
||||
*
|
||||
* @param conn the netconn from which to receive data
|
||||
@@ -437,12 +591,13 @@ err_t
|
||||
netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
|
||||
{
|
||||
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
|
||||
netconn_type(conn) == NETCONN_TCP, return ERR_ARG;);
|
||||
NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
|
||||
|
||||
return netconn_recv_data(conn, (void **)new_buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_common
|
||||
* Receive data (in form of a netbuf containing a packet buffer) from a netconn
|
||||
*
|
||||
* @param conn the netconn from which to receive data
|
||||
@@ -461,11 +616,10 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
|
||||
LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
|
||||
*new_buf = NULL;
|
||||
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
|
||||
|
||||
#if LWIP_TCP
|
||||
#if (LWIP_UDP || LWIP_RAW)
|
||||
if (conn->type == NETCONN_TCP)
|
||||
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
|
||||
#endif /* (LWIP_UDP || LWIP_RAW) */
|
||||
{
|
||||
struct pbuf *p = NULL;
|
||||
@@ -473,7 +627,6 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
|
||||
|
||||
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
|
||||
if (buf == NULL) {
|
||||
NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
@@ -487,7 +640,7 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
|
||||
buf->p = p;
|
||||
buf->ptr = p;
|
||||
buf->port = 0;
|
||||
ip_addr_set_any(&buf->addr);
|
||||
ip_addr_set_zero(&buf->addr);
|
||||
*new_buf = buf;
|
||||
/* don't set conn->last_err: it's only ERR_OK, anyway */
|
||||
return ERR_OK;
|
||||
@@ -504,38 +657,7 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
|
||||
}
|
||||
|
||||
/**
|
||||
* TCP: update the receive window: by calling this, the application
|
||||
* tells the stack that it has processed data and is able to accept
|
||||
* new data.
|
||||
* ATTENTION: use with care, this is mainly used for sockets!
|
||||
* Can only be used when calling netconn_set_noautorecved(conn, 1) before.
|
||||
*
|
||||
* @param conn the netconn for which to update the receive window
|
||||
* @param length amount of data processed (ATTENTION: this must be accurate!)
|
||||
*/
|
||||
void
|
||||
netconn_recved(struct netconn *conn, u32_t length)
|
||||
{
|
||||
#if LWIP_TCP
|
||||
if ((conn != NULL) && (conn->type == NETCONN_TCP) &&
|
||||
(netconn_get_noautorecved(conn))) {
|
||||
struct api_msg msg;
|
||||
/* Let the stack know that we have taken the data. */
|
||||
/* TODO: Speedup: Don't block and wait for the answer here
|
||||
(to prevent multiple thread-switches). */
|
||||
msg.function = do_recv;
|
||||
msg.msg.conn = conn;
|
||||
msg.msg.msg.r.len = length;
|
||||
/* don't care for the return value of do_recv */
|
||||
TCPIP_APIMSG(&msg);
|
||||
}
|
||||
#else /* LWIP_TCP */
|
||||
LWIP_UNUSED_ARG(conn);
|
||||
LWIP_UNUSED_ARG(length);
|
||||
#endif /* LWIP_TCP */
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_udp
|
||||
* Send data (in form of a netbuf) to a specific remote IP address and port.
|
||||
* Only to be used for UDP and RAW netconns (not TCP).
|
||||
*
|
||||
@@ -546,7 +668,7 @@ netconn_recved(struct netconn *conn, u32_t length)
|
||||
* @return ERR_OK if data was sent, any other err_t on error
|
||||
*/
|
||||
err_t
|
||||
netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
|
||||
netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
if (buf != NULL) {
|
||||
ip_addr_set(&buf->addr, addr);
|
||||
@@ -557,6 +679,7 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_udp
|
||||
* Send data over a UDP or RAW netconn (that is already connected).
|
||||
*
|
||||
* @param conn the UDP or RAW netconn over which to send data
|
||||
@@ -566,22 +689,23 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t
|
||||
err_t
|
||||
netconn_send(struct netconn *conn, struct netbuf *buf)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
|
||||
LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
|
||||
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
|
||||
msg.function = do_send;
|
||||
msg.msg.conn = conn;
|
||||
msg.msg.msg.b = buf;
|
||||
err = TCPIP_APIMSG(&msg);
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
API_MSG_VAR_REF(msg).msg.b = buf;
|
||||
err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_tcp
|
||||
* Send data over a TCP netconn.
|
||||
*
|
||||
* @param conn the TCP netconn over which to send data
|
||||
@@ -590,7 +714,7 @@ netconn_send(struct netconn *conn, struct netbuf *buf)
|
||||
* @param apiflags combination of following flags :
|
||||
* - NETCONN_COPY: data will be copied into memory belonging to the stack
|
||||
* - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
|
||||
* - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
|
||||
* - NETCONN_DONTBLOCK: only write the data if all data can be written at once
|
||||
* @param bytes_written pointer to a location that receives the number of written bytes
|
||||
* @return ERR_OK if data was sent, any other err_t on error
|
||||
*/
|
||||
@@ -598,12 +722,12 @@ err_t
|
||||
netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
|
||||
u8_t apiflags, size_t *bytes_written)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
u8_t dontblock;
|
||||
|
||||
LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;);
|
||||
LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
|
||||
if (size == 0) {
|
||||
return ERR_OK;
|
||||
}
|
||||
@@ -614,26 +738,26 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
/* non-blocking write sends as much */
|
||||
msg.function = do_write;
|
||||
msg.msg.conn = conn;
|
||||
msg.msg.msg.w.dataptr = dataptr;
|
||||
msg.msg.msg.w.apiflags = apiflags;
|
||||
msg.msg.msg.w.len = size;
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
API_MSG_VAR_REF(msg).msg.w.dataptr = dataptr;
|
||||
API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
|
||||
API_MSG_VAR_REF(msg).msg.w.len = size;
|
||||
#if LWIP_SO_SNDTIMEO
|
||||
if (conn->send_timeout != 0) {
|
||||
/* get the time we started, which is later compared to
|
||||
sys_now() + conn->send_timeout */
|
||||
msg.msg.msg.w.time_started = sys_now();
|
||||
API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
|
||||
} else {
|
||||
msg.msg.msg.w.time_started = 0;
|
||||
API_MSG_VAR_REF(msg).msg.w.time_started = 0;
|
||||
}
|
||||
#endif /* LWIP_SO_SNDTIMEO */
|
||||
|
||||
/* For locking the core: this _can_ be delayed on low memory/low send buffer,
|
||||
but if it is, this is done inside api_msg.c:do_write(), so we can use the
|
||||
non-blocking version here. */
|
||||
err = TCPIP_APIMSG(&msg);
|
||||
err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
|
||||
if ((err == ERR_OK) && (bytes_written != NULL)) {
|
||||
if (dontblock
|
||||
#if LWIP_SO_SNDTIMEO
|
||||
@@ -641,19 +765,20 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
|
||||
#endif /* LWIP_SO_SNDTIMEO */
|
||||
) {
|
||||
/* nonblocking write: maybe the data has been sent partly */
|
||||
*bytes_written = msg.msg.msg.w.len;
|
||||
*bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
|
||||
} else {
|
||||
/* blocking call succeeded: all data has been sent if it */
|
||||
*bytes_written = size;
|
||||
}
|
||||
}
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close ot shutdown a TCP netconn (doesn't delete it).
|
||||
* @ingroup netconn_tcp
|
||||
* Close or shutdown a TCP netconn (doesn't delete it).
|
||||
*
|
||||
* @param conn the TCP netconn to close or shutdown
|
||||
* @param how fully close or only shutdown one side?
|
||||
@@ -662,24 +787,34 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
|
||||
static err_t
|
||||
netconn_close_shutdown(struct netconn *conn, u8_t how)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
LWIP_UNUSED_ARG(how);
|
||||
|
||||
LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
|
||||
msg.function = do_close;
|
||||
msg.msg.conn = conn;
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
#if LWIP_TCP
|
||||
/* shutting down both ends is the same as closing */
|
||||
msg.msg.msg.sd.shut = how;
|
||||
/* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
|
||||
don't use TCPIP_APIMSG here */
|
||||
err = tcpip_apimsg(&msg);
|
||||
API_MSG_VAR_REF(msg).msg.sd.shut = how;
|
||||
#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
|
||||
/* get the time we started, which is later compared to
|
||||
sys_now() + conn->send_timeout */
|
||||
API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
|
||||
#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||
API_MSG_VAR_REF(msg).msg.sd.polls_left =
|
||||
((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
|
||||
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||
#endif /* LWIP_TCP */
|
||||
err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_tcp
|
||||
* Close a TCP netconn (doesn't delete it).
|
||||
*
|
||||
* @param conn the TCP netconn to close
|
||||
@@ -693,9 +828,12 @@ netconn_close(struct netconn *conn)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netconn_tcp
|
||||
* Shut down one or both sides of a TCP netconn (doesn't delete it).
|
||||
*
|
||||
* @param conn the TCP netconn to shut down
|
||||
* @param shut_rx shut down the RX side (no more read possible after this)
|
||||
* @param shut_tx shut down the TX side (no more write possible after this)
|
||||
* @return ERR_OK if the netconn was closed, any other err_t on error
|
||||
*/
|
||||
err_t
|
||||
@@ -704,8 +842,9 @@ netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
|
||||
return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
|
||||
}
|
||||
|
||||
#if LWIP_IGMP
|
||||
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
|
||||
/**
|
||||
* @ingroup netconn_udp
|
||||
* Join multicast groups for UDP netconns.
|
||||
*
|
||||
* @param conn the UDP netconn for which to change multicast addresses
|
||||
@@ -717,64 +856,138 @@ netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
|
||||
*/
|
||||
err_t
|
||||
netconn_join_leave_group(struct netconn *conn,
|
||||
ip_addr_t *multiaddr,
|
||||
ip_addr_t *netif_addr,
|
||||
const ip_addr_t *multiaddr,
|
||||
const ip_addr_t *netif_addr,
|
||||
enum netconn_igmp join_or_leave)
|
||||
{
|
||||
struct api_msg msg;
|
||||
API_MSG_VAR_DECLARE(msg);
|
||||
err_t err;
|
||||
|
||||
LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||
|
||||
msg.function = do_join_leave_group;
|
||||
msg.msg.conn = conn;
|
||||
msg.msg.msg.jl.multiaddr = multiaddr;
|
||||
msg.msg.msg.jl.netif_addr = netif_addr;
|
||||
msg.msg.msg.jl.join_or_leave = join_or_leave;
|
||||
err = TCPIP_APIMSG(&msg);
|
||||
API_MSG_VAR_ALLOC(msg);
|
||||
|
||||
/* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
|
||||
if (multiaddr == NULL) {
|
||||
multiaddr = IP_ADDR_ANY;
|
||||
}
|
||||
if (netif_addr == NULL) {
|
||||
netif_addr = IP_ADDR_ANY;
|
||||
}
|
||||
|
||||
API_MSG_VAR_REF(msg).conn = conn;
|
||||
API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
|
||||
API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
|
||||
API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
|
||||
err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
|
||||
API_MSG_VAR_FREE(msg);
|
||||
|
||||
NETCONN_SET_SAFE_ERR(conn, err);
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_IGMP */
|
||||
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
|
||||
|
||||
#if LWIP_DNS
|
||||
/**
|
||||
* @ingroup netconn_common
|
||||
* Execute a DNS query, only one IP address is returned
|
||||
*
|
||||
* @param name a string representation of the DNS host name to query
|
||||
* @param addr a preallocated ip_addr_t where to store the resolved IP address
|
||||
* @param dns_addrtype IP address type (IPv4 / IPv6)
|
||||
* @return ERR_OK: resolving succeeded
|
||||
* ERR_MEM: memory error, try again later
|
||||
* ERR_ARG: dns client not initialized or invalid hostname
|
||||
* ERR_VAL: dns server response was invalid
|
||||
*/
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
err_t
|
||||
netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
|
||||
#else
|
||||
err_t
|
||||
netconn_gethostbyname(const char *name, ip_addr_t *addr)
|
||||
#endif
|
||||
{
|
||||
struct dns_api_msg msg;
|
||||
err_t err;
|
||||
API_VAR_DECLARE(struct dns_api_msg, msg);
|
||||
#if !LWIP_MPU_COMPATIBLE
|
||||
sys_sem_t sem;
|
||||
#endif /* LWIP_MPU_COMPATIBLE */
|
||||
err_t err;
|
||||
|
||||
LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
|
||||
|
||||
err = sys_sem_new(&sem, 0);
|
||||
if (err != ERR_OK) {
|
||||
return err;
|
||||
#if LWIP_MPU_COMPATIBLE
|
||||
if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
#endif
|
||||
|
||||
msg.name = name;
|
||||
msg.addr = addr;
|
||||
API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
|
||||
#if LWIP_MPU_COMPATIBLE
|
||||
strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1);
|
||||
API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0;
|
||||
#else /* LWIP_MPU_COMPATIBLE */
|
||||
msg.err = &err;
|
||||
msg.sem = &sem;
|
||||
API_VAR_REF(msg).addr = API_VAR_REF(addr);
|
||||
API_VAR_REF(msg).name = name;
|
||||
#endif /* LWIP_MPU_COMPATIBLE */
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
API_VAR_REF(msg).dns_addrtype = dns_addrtype;
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
#if LWIP_NETCONN_SEM_PER_THREAD
|
||||
API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
|
||||
#else /* LWIP_NETCONN_SEM_PER_THREAD*/
|
||||
err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
|
||||
if (err != ERR_OK) {
|
||||
API_VAR_FREE(MEMP_DNS_API_MSG, msg);
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
tcpip_callback(do_gethostbyname, &msg);
|
||||
sys_sem_wait(&sem);
|
||||
sys_sem_free(&sem);
|
||||
err = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
|
||||
if (err != ERR_OK) {
|
||||
#if !LWIP_NETCONN_SEM_PER_THREAD
|
||||
sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
|
||||
#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
|
||||
API_VAR_FREE(MEMP_DNS_API_MSG, msg);
|
||||
return err;
|
||||
}
|
||||
sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
|
||||
#if !LWIP_NETCONN_SEM_PER_THREAD
|
||||
sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
|
||||
#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
#if LWIP_MPU_COMPATIBLE
|
||||
*addr = msg->addr;
|
||||
err = msg->err;
|
||||
#endif /* LWIP_MPU_COMPATIBLE */
|
||||
|
||||
API_VAR_FREE(MEMP_DNS_API_MSG, msg);
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_DNS*/
|
||||
|
||||
#if LWIP_NETCONN_SEM_PER_THREAD
|
||||
void
|
||||
netconn_thread_init(void)
|
||||
{
|
||||
sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
|
||||
if ((sem == NULL) || !sys_sem_valid(sem)) {
|
||||
/* call alloc only once */
|
||||
LWIP_NETCONN_THREAD_SEM_ALLOC();
|
||||
LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
netconn_thread_cleanup(void)
|
||||
{
|
||||
sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
|
||||
if ((sem != NULL) && sys_sem_valid(sem)) {
|
||||
/* call free only once */
|
||||
LWIP_NETCONN_THREAD_SEM_FREE();
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
#endif /* LWIP_NETCONN */
|
||||
@@ -36,6 +36,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup netbuf Network buffers
|
||||
* @ingroup netconn
|
||||
* Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
|
||||
* to avoid copying data around.\n
|
||||
* Buffers must not be shared accross multiple threads, all functions except
|
||||
* netbuf_new() and netbuf_delete() are not thread-safe.
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
|
||||
@@ -46,6 +55,7 @@
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Create (allocate) and initialize a new netbuf.
|
||||
* The netbuf doesn't yet contain a packet buffer!
|
||||
*
|
||||
@@ -61,7 +71,7 @@ netbuf *netbuf_new(void)
|
||||
if (buf != NULL) {
|
||||
buf->p = NULL;
|
||||
buf->ptr = NULL;
|
||||
ip_addr_set_any(&buf->addr);
|
||||
ip_addr_set_zero(&buf->addr);
|
||||
buf->port = 0;
|
||||
#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
|
||||
#if LWIP_CHECKSUM_ON_COPY
|
||||
@@ -69,7 +79,7 @@ netbuf *netbuf_new(void)
|
||||
#endif /* LWIP_CHECKSUM_ON_COPY */
|
||||
buf->toport_chksum = 0;
|
||||
#if LWIP_NETBUF_RECVINFO
|
||||
ip_addr_set_any(&buf->toaddr);
|
||||
ip_addr_set_zero(&buf->toaddr);
|
||||
#endif /* LWIP_NETBUF_RECVINFO */
|
||||
#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
|
||||
return buf;
|
||||
@@ -79,6 +89,7 @@ netbuf *netbuf_new(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Deallocate a netbuf allocated by netbuf_new().
|
||||
*
|
||||
* @param buf pointer to a netbuf allocated by netbuf_new()
|
||||
@@ -96,6 +107,7 @@ netbuf_delete(struct netbuf *buf)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Allocate memory for a packet buffer for a given netbuf.
|
||||
*
|
||||
* @param buf the netbuf for which to allocate a packet buffer
|
||||
@@ -123,6 +135,7 @@ netbuf_alloc(struct netbuf *buf, u16_t size)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Free the packet buffer included in a netbuf
|
||||
*
|
||||
* @param buf pointer to the netbuf which contains the packet buffer to free
|
||||
@@ -138,6 +151,7 @@ netbuf_free(struct netbuf *buf)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Let a netbuf reference existing (non-volatile) data.
|
||||
*
|
||||
* @param buf netbuf which should reference the data
|
||||
@@ -158,13 +172,14 @@ netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
|
||||
buf->ptr = NULL;
|
||||
return ERR_MEM;
|
||||
}
|
||||
buf->p->payload = (void*)dataptr;
|
||||
((struct pbuf_rom*)buf->p)->payload = dataptr;
|
||||
buf->p->len = buf->p->tot_len = size;
|
||||
buf->ptr = buf->p;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Chain one netbuf to another (@see pbuf_chain)
|
||||
*
|
||||
* @param head the first netbuf
|
||||
@@ -181,12 +196,13 @@ netbuf_chain(struct netbuf *head, struct netbuf *tail)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Get the data pointer and length of the data inside a netbuf.
|
||||
*
|
||||
* @param buf netbuf to get the data from
|
||||
* @param dataptr pointer to a void pointer where to store the data pointer
|
||||
* @param len pointer to an u16_t where the length of the data is stored
|
||||
* @return ERR_OK if the information was retreived,
|
||||
* @return ERR_OK if the information was retrieved,
|
||||
* ERR_BUF on error.
|
||||
*/
|
||||
err_t
|
||||
@@ -205,6 +221,7 @@ netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Move the current data pointer of a packet buffer contained in a netbuf
|
||||
* to the next part.
|
||||
* The packet buffer itself is not modified.
|
||||
@@ -229,6 +246,7 @@ netbuf_next(struct netbuf *buf)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Move the current data pointer of a packet buffer contained in a netbuf
|
||||
* to the beginning of the packet.
|
||||
* The packet buffer itself is not modified.
|
||||
@@ -33,6 +33,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup netdbapi NETDB API
|
||||
* @ingroup socket
|
||||
*/
|
||||
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
#if LWIP_DNS && LWIP_SOCKET
|
||||
@@ -92,6 +98,7 @@ lwip_gethostbyname(const char *name)
|
||||
HOSTENT_STORAGE char *s_aliases;
|
||||
HOSTENT_STORAGE ip_addr_t s_hostent_addr;
|
||||
HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
|
||||
HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
|
||||
|
||||
/* query host IP address */
|
||||
err = netconn_gethostbyname(name, &addr);
|
||||
@@ -105,7 +112,10 @@ lwip_gethostbyname(const char *name)
|
||||
s_hostent_addr = addr;
|
||||
s_phostent_addr[0] = &s_hostent_addr;
|
||||
s_phostent_addr[1] = NULL;
|
||||
s_hostent.h_name = (char*)name;
|
||||
strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
|
||||
s_hostname[DNS_MAX_NAME_LENGTH] = 0;
|
||||
s_hostent.h_name = s_hostname;
|
||||
s_aliases = NULL;
|
||||
s_hostent.h_aliases = &s_aliases;
|
||||
s_hostent.h_addrtype = AF_INET;
|
||||
s_hostent.h_length = sizeof(ip_addr_t);
|
||||
@@ -114,22 +124,16 @@ lwip_gethostbyname(const char *name)
|
||||
#if DNS_DEBUG
|
||||
/* dump hostent */
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases));
|
||||
if (s_hostent.h_aliases != NULL) {
|
||||
u8_t idx;
|
||||
for ( idx=0; s_hostent.h_aliases[idx]; idx++) {
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx]));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx]));
|
||||
}
|
||||
}
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", (void*)s_hostent.h_aliases));
|
||||
/* h_aliases are always empty */
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", (void*)s_hostent.h_addr_list));
|
||||
if (s_hostent.h_addr_list != NULL) {
|
||||
u8_t idx;
|
||||
for (idx=0; s_hostent.h_addr_list[idx]; idx++) {
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
|
||||
}
|
||||
}
|
||||
#endif /* DNS_DEBUG */
|
||||
@@ -262,6 +266,8 @@ lwip_freeaddrinfo(struct addrinfo *ai)
|
||||
* @param hints structure containing input values that set socktype and protocol
|
||||
* @param res pointer to a pointer where to store the result (set to NULL on failure)
|
||||
* @return 0 on success, non-zero on failure
|
||||
*
|
||||
* @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
|
||||
*/
|
||||
int
|
||||
lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
@@ -270,10 +276,11 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
err_t err;
|
||||
ip_addr_t addr;
|
||||
struct addrinfo *ai;
|
||||
struct sockaddr_in *sa = NULL;
|
||||
struct sockaddr_storage *sa = NULL;
|
||||
int port_nr = 0;
|
||||
size_t total_size;
|
||||
size_t namelen = 0;
|
||||
int ai_family;
|
||||
|
||||
if (res == NULL) {
|
||||
return EAI_FAIL;
|
||||
@@ -283,9 +290,25 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
return EAI_NONAME;
|
||||
}
|
||||
|
||||
if (hints != NULL) {
|
||||
ai_family = hints->ai_family;
|
||||
if ((ai_family != AF_UNSPEC)
|
||||
#if LWIP_IPV4
|
||||
&& (ai_family != AF_INET)
|
||||
#endif /* LWIP_IPV4 */
|
||||
#if LWIP_IPV6
|
||||
&& (ai_family != AF_INET6)
|
||||
#endif /* LWIP_IPV6 */
|
||||
) {
|
||||
return EAI_FAMILY;
|
||||
}
|
||||
} else {
|
||||
ai_family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
if (servname != NULL) {
|
||||
/* service name specified: convert to port number
|
||||
* @todo?: currently, only ASCII integers (port numbers) are supported! */
|
||||
* @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
|
||||
port_nr = atoi(servname);
|
||||
if ((port_nr <= 0) || (port_nr > 0xffff)) {
|
||||
return EAI_SERVICE;
|
||||
@@ -294,19 +317,49 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
|
||||
if (nodename != NULL) {
|
||||
/* service location specified, try to resolve */
|
||||
err = netconn_gethostbyname(nodename, &addr);
|
||||
if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
|
||||
/* no DNS lookup, just parse for an address string */
|
||||
if (!ipaddr_aton(nodename, &addr)) {
|
||||
return EAI_NONAME;
|
||||
}
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
|
||||
(IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
|
||||
return EAI_NONAME;
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
} else {
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
/* AF_UNSPEC: prefer IPv4 */
|
||||
u8_t type = NETCONN_DNS_IPV4_IPV6;
|
||||
if (ai_family == AF_INET) {
|
||||
type = NETCONN_DNS_IPV4;
|
||||
} else if (ai_family == AF_INET6) {
|
||||
type = NETCONN_DNS_IPV6;
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
|
||||
if (err != ERR_OK) {
|
||||
return EAI_FAIL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* service location specified, use loopback address */
|
||||
ip_addr_set_loopback(&addr);
|
||||
if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
|
||||
ip_addr_set_any(ai_family == AF_INET6, &addr);
|
||||
} else {
|
||||
ip_addr_set_loopback(ai_family == AF_INET6, &addr);
|
||||
}
|
||||
}
|
||||
|
||||
total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
|
||||
total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
|
||||
if (nodename != NULL) {
|
||||
namelen = strlen(nodename);
|
||||
LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1);
|
||||
if (namelen > DNS_MAX_NAME_LENGTH) {
|
||||
/* invalid name length */
|
||||
return EAI_FAIL;
|
||||
}
|
||||
LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
|
||||
total_size += namelen + 1;
|
||||
}
|
||||
/* If this fails, please report to lwip-devel! :-) */
|
||||
@@ -314,18 +367,34 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
total_size <= NETDB_ELEM_SIZE);
|
||||
ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
|
||||
if (ai == NULL) {
|
||||
goto memerr;
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
memset(ai, 0, total_size);
|
||||
sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo));
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo));
|
||||
if (IP_IS_V6_VAL(addr)) {
|
||||
#if LWIP_IPV6
|
||||
struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
|
||||
/* set up sockaddr */
|
||||
inet_addr_from_ipaddr(&sa->sin_addr, &addr);
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_len = sizeof(struct sockaddr_in);
|
||||
sa->sin_port = htons((u16_t)port_nr);
|
||||
inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
|
||||
sa6->sin6_family = AF_INET6;
|
||||
sa6->sin6_len = sizeof(struct sockaddr_in6);
|
||||
sa6->sin6_port = htons((u16_t)port_nr);
|
||||
ai->ai_family = AF_INET6;
|
||||
#endif /* LWIP_IPV6 */
|
||||
} else {
|
||||
#if LWIP_IPV4
|
||||
struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
|
||||
/* set up sockaddr */
|
||||
inet_addr_from_ipaddr(&sa4->sin_addr, ip_2_ip4(&addr));
|
||||
sa4->sin_family = AF_INET;
|
||||
sa4->sin_len = sizeof(struct sockaddr_in);
|
||||
sa4->sin_port = htons((u16_t)port_nr);
|
||||
ai->ai_family = AF_INET;
|
||||
#endif /* LWIP_IPV4 */
|
||||
}
|
||||
|
||||
/* set up addrinfo */
|
||||
ai->ai_family = AF_INET;
|
||||
if (hints != NULL) {
|
||||
/* copy socktype & protocol from hints if specified */
|
||||
ai->ai_socktype = hints->ai_socktype;
|
||||
@@ -333,21 +402,16 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
}
|
||||
if (nodename != NULL) {
|
||||
/* copy nodename to canonname if specified */
|
||||
ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
|
||||
ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
|
||||
MEMCPY(ai->ai_canonname, nodename, namelen);
|
||||
ai->ai_canonname[namelen] = 0;
|
||||
}
|
||||
ai->ai_addrlen = sizeof(struct sockaddr_in);
|
||||
ai->ai_addrlen = sizeof(struct sockaddr_storage);
|
||||
ai->ai_addr = (struct sockaddr*)sa;
|
||||
|
||||
*res = ai;
|
||||
|
||||
return 0;
|
||||
memerr:
|
||||
if (ai != NULL) {
|
||||
memp_free(MEMP_NETDB, ai);
|
||||
}
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
|
||||
#endif /* LWIP_DNS && LWIP_SOCKET */
|
||||
224
ext/lwip/src/api/netifapi.c
Normal file
224
ext/lwip/src/api/netifapi.c
Normal file
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* @file
|
||||
* Network Interface Sequential API module
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup netifapi NETIF API
|
||||
* @ingroup threadsafe_api
|
||||
* Thread-safe functions to be called from non-TCPIP threads
|
||||
*
|
||||
* @defgroup netifapi_netif NETIF related
|
||||
* @ingroup netifapi
|
||||
* To be called from non-TCPIP threads
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/netifapi.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
#define NETIFAPI_VAR_REF(name) API_VAR_REF(name)
|
||||
#define NETIFAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct netifapi_msg, name)
|
||||
#define NETIFAPI_VAR_ALLOC(name) API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
|
||||
#define NETIFAPI_VAR_FREE(name) API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
|
||||
|
||||
/**
|
||||
* Call netif_add() inside the tcpip_thread context.
|
||||
*/
|
||||
static err_t
|
||||
netifapi_do_netif_add(struct tcpip_api_call_data *m)
|
||||
{
|
||||
/* cast through void* to silence alignment warnings.
|
||||
* We know it works because the structs have been instantiated as struct netifapi_msg */
|
||||
struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
|
||||
|
||||
if (!netif_add( msg->netif,
|
||||
#if LWIP_IPV4
|
||||
API_EXPR_REF(msg->msg.add.ipaddr),
|
||||
API_EXPR_REF(msg->msg.add.netmask),
|
||||
API_EXPR_REF(msg->msg.add.gw),
|
||||
#endif /* LWIP_IPV4 */
|
||||
msg->msg.add.state,
|
||||
msg->msg.add.init,
|
||||
msg->msg.add.input)) {
|
||||
return ERR_IF;
|
||||
} else {
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/**
|
||||
* Call netif_set_addr() inside the tcpip_thread context.
|
||||
*/
|
||||
static err_t
|
||||
netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
|
||||
{
|
||||
/* cast through void* to silence alignment warnings.
|
||||
* We know it works because the structs have been instantiated as struct netifapi_msg */
|
||||
struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
|
||||
|
||||
netif_set_addr( msg->netif,
|
||||
API_EXPR_REF(msg->msg.add.ipaddr),
|
||||
API_EXPR_REF(msg->msg.add.netmask),
|
||||
API_EXPR_REF(msg->msg.add.gw));
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/**
|
||||
* Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
|
||||
* tcpip_thread context.
|
||||
*/
|
||||
static err_t
|
||||
netifapi_do_netif_common(struct tcpip_api_call_data *m)
|
||||
{
|
||||
/* cast through void* to silence alignment warnings.
|
||||
* We know it works because the structs have been instantiated as struct netifapi_msg */
|
||||
struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
|
||||
|
||||
if (msg->msg.common.errtfunc != NULL) {
|
||||
return msg->msg.common.errtfunc(msg->netif);
|
||||
} else {
|
||||
msg->msg.common.voidfunc(msg->netif);
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netifapi_netif
|
||||
* Call netif_add() in a thread-safe way by running that function inside the
|
||||
* tcpip_thread context.
|
||||
*
|
||||
* @note for params @see netif_add()
|
||||
*/
|
||||
err_t
|
||||
netifapi_netif_add(struct netif *netif,
|
||||
#if LWIP_IPV4
|
||||
const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
|
||||
#endif /* LWIP_IPV4 */
|
||||
void *state, netif_init_fn init, netif_input_fn input)
|
||||
{
|
||||
err_t err;
|
||||
NETIFAPI_VAR_DECLARE(msg);
|
||||
NETIFAPI_VAR_ALLOC(msg);
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (ipaddr == NULL) {
|
||||
ipaddr = IP4_ADDR_ANY;
|
||||
}
|
||||
if (netmask == NULL) {
|
||||
netmask = IP4_ADDR_ANY;
|
||||
}
|
||||
if (gw == NULL) {
|
||||
gw = IP4_ADDR_ANY;
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
NETIFAPI_VAR_REF(msg).netif = netif;
|
||||
#if LWIP_IPV4
|
||||
NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
|
||||
#endif /* LWIP_IPV4 */
|
||||
NETIFAPI_VAR_REF(msg).msg.add.state = state;
|
||||
NETIFAPI_VAR_REF(msg).msg.add.init = init;
|
||||
NETIFAPI_VAR_REF(msg).msg.add.input = input;
|
||||
err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
|
||||
NETIFAPI_VAR_FREE(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/**
|
||||
* @ingroup netifapi_netif
|
||||
* Call netif_set_addr() in a thread-safe way by running that function inside the
|
||||
* tcpip_thread context.
|
||||
*
|
||||
* @note for params @see netif_set_addr()
|
||||
*/
|
||||
err_t
|
||||
netifapi_netif_set_addr(struct netif *netif,
|
||||
const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw)
|
||||
{
|
||||
err_t err;
|
||||
NETIFAPI_VAR_DECLARE(msg);
|
||||
NETIFAPI_VAR_ALLOC(msg);
|
||||
|
||||
if (ipaddr == NULL) {
|
||||
ipaddr = IP4_ADDR_ANY;
|
||||
}
|
||||
if (netmask == NULL) {
|
||||
netmask = IP4_ADDR_ANY;
|
||||
}
|
||||
if (gw == NULL) {
|
||||
gw = IP4_ADDR_ANY;
|
||||
}
|
||||
|
||||
NETIFAPI_VAR_REF(msg).netif = netif;
|
||||
NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
|
||||
err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
|
||||
NETIFAPI_VAR_FREE(msg);
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/**
|
||||
* call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
|
||||
* way by running that function inside the tcpip_thread context.
|
||||
*
|
||||
* @note use only for functions where there is only "netif" parameter.
|
||||
*/
|
||||
err_t
|
||||
netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
|
||||
netifapi_errt_fn errtfunc)
|
||||
{
|
||||
err_t err;
|
||||
NETIFAPI_VAR_DECLARE(msg);
|
||||
NETIFAPI_VAR_ALLOC(msg);
|
||||
|
||||
NETIFAPI_VAR_REF(msg).netif = netif;
|
||||
NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
|
||||
NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
|
||||
err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
|
||||
NETIFAPI_VAR_FREE(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* LWIP_NETIF_API */
|
||||
2803
ext/lwip/src/api/sockets.c
Normal file
2803
ext/lwip/src/api/sockets.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -40,14 +40,19 @@
|
||||
|
||||
#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/init.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "netif/ppp_oe.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/etharp.h"
|
||||
|
||||
#define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name)
|
||||
#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
|
||||
#define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
|
||||
#define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
|
||||
|
||||
/* global variables */
|
||||
static tcpip_init_done_fn tcpip_init_done;
|
||||
@@ -87,35 +92,31 @@ tcpip_thread(void *arg)
|
||||
/* wait for a message, timeouts are processed while waiting */
|
||||
sys_timeouts_mbox_fetch(&mbox, (void **)&msg);
|
||||
LOCK_TCPIP_CORE();
|
||||
if (msg == NULL) {
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
|
||||
LWIP_ASSERT("tcpip_thread: invalid message", 0);
|
||||
continue;
|
||||
}
|
||||
switch (msg->type) {
|
||||
#if LWIP_NETCONN
|
||||
#if !LWIP_TCPIP_CORE_LOCKING
|
||||
case TCPIP_MSG_API:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
|
||||
msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
|
||||
msg->msg.api_msg.function(msg->msg.api_msg.msg);
|
||||
break;
|
||||
#endif /* LWIP_NETCONN */
|
||||
case TCPIP_MSG_API_CALL:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
|
||||
msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
|
||||
sys_sem_signal(msg->msg.api_call.sem);
|
||||
break;
|
||||
#endif /* !LWIP_TCPIP_CORE_LOCKING */
|
||||
|
||||
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
|
||||
case TCPIP_MSG_INPKT:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
|
||||
#if LWIP_ETHERNET
|
||||
if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
|
||||
ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
|
||||
} else
|
||||
#endif /* LWIP_ETHERNET */
|
||||
{
|
||||
ip_input(msg->msg.inp.p, msg->msg.inp.netif);
|
||||
}
|
||||
msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
|
||||
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
|
||||
break;
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
|
||||
|
||||
#if LWIP_NETIF_API
|
||||
case TCPIP_MSG_NETIFAPI:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
|
||||
msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
|
||||
break;
|
||||
#endif /* LWIP_NETIF_API */
|
||||
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
|
||||
|
||||
#if LWIP_TCPIP_TIMEOUT
|
||||
case TCPIP_MSG_TIMEOUT:
|
||||
@@ -152,34 +153,25 @@ tcpip_thread(void *arg)
|
||||
/**
|
||||
* Pass a received packet to tcpip_thread for input processing
|
||||
*
|
||||
* @param p the received packet, p->payload pointing to the Ethernet header or
|
||||
* to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
|
||||
* NETIF_FLAG_ETHERNET flags)
|
||||
* @param p the received packet
|
||||
* @param inp the network interface on which the packet was received
|
||||
* @param input_fn input function to call
|
||||
*/
|
||||
err_t
|
||||
tcpip_input(struct pbuf *p, struct netif *inp)
|
||||
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
|
||||
{
|
||||
#if LWIP_TCPIP_CORE_LOCKING_INPUT
|
||||
err_t ret;
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp));
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
|
||||
LOCK_TCPIP_CORE();
|
||||
#if LWIP_ETHERNET
|
||||
if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
|
||||
ret = ethernet_input(p, inp);
|
||||
} else
|
||||
#endif /* LWIP_ETHERNET */
|
||||
{
|
||||
ret = ip_input(p, inp);
|
||||
}
|
||||
ret = input_fn(p, inp);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
return ret;
|
||||
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
if (!sys_mbox_valid(&mbox)) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
|
||||
if (msg == NULL) {
|
||||
return ERR_MEM;
|
||||
@@ -188,6 +180,7 @@ tcpip_input(struct pbuf *p, struct netif *inp)
|
||||
msg->type = TCPIP_MSG_INPKT;
|
||||
msg->msg.inp.p = p;
|
||||
msg->msg.inp.netif = inp;
|
||||
msg->msg.inp.input_fn = input_fn;
|
||||
if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
|
||||
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
|
||||
return ERR_MEM;
|
||||
@@ -196,13 +189,35 @@ tcpip_input(struct pbuf *p, struct netif *inp)
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup lwip_os
|
||||
* Pass a received packet to tcpip_thread for input processing with
|
||||
* ethernet_input or ip_input. Don't call directly, pass to netif_add()
|
||||
* and call netif->input().
|
||||
*
|
||||
* @param p the received packet, p->payload pointing to the Ethernet header or
|
||||
* to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
|
||||
* NETIF_FLAG_ETHERNET flags)
|
||||
* @param inp the network interface on which the packet was received
|
||||
*/
|
||||
err_t
|
||||
tcpip_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
#if LWIP_ETHERNET
|
||||
if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
|
||||
return tcpip_inpkt(p, inp, ethernet_input);
|
||||
} else
|
||||
#endif /* LWIP_ETHERNET */
|
||||
return tcpip_inpkt(p, inp, ip_input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a specific function in the thread context of
|
||||
* tcpip_thread for easy access synchronization.
|
||||
* A function called in that way may access lwIP core code
|
||||
* without fearing concurrent access.
|
||||
*
|
||||
* @param f the function to call
|
||||
* @param function the function to call
|
||||
* @param ctx parameter passed to f
|
||||
* @param block 1 to block until the request is posted, 0 to non-blocking mode
|
||||
* @return ERR_OK if the function was called, another err_t if not
|
||||
@@ -212,7 +227,8 @@ tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
if (sys_mbox_valid(&mbox)) {
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
return ERR_MEM;
|
||||
@@ -231,8 +247,6 @@ tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
#if LWIP_TCPIP_TIMEOUT
|
||||
/**
|
||||
@@ -248,7 +262,8 @@ tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
if (sys_mbox_valid(&mbox)) {
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
return ERR_MEM;
|
||||
@@ -261,8 +276,6 @@ tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
|
||||
sys_mbox_post(&mbox, msg);
|
||||
return ERR_OK;
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* call sys_untimeout in tcpip_thread
|
||||
@@ -277,7 +290,8 @@ tcpip_untimeout(sys_timeout_handler h, void *arg)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
if (sys_mbox_valid(&mbox)) {
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
return ERR_MEM;
|
||||
@@ -289,113 +303,98 @@ tcpip_untimeout(sys_timeout_handler h, void *arg)
|
||||
sys_mbox_post(&mbox, msg);
|
||||
return ERR_OK;
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
#endif /* LWIP_TCPIP_TIMEOUT */
|
||||
|
||||
#if LWIP_NETCONN
|
||||
|
||||
/**
|
||||
* Call the lower part of a netconn_* function
|
||||
* This function is then running in the thread context
|
||||
* of tcpip_thread and has exclusive access to lwIP core code.
|
||||
* Sends a message to TCPIP thread to call a function. Caller thread blocks on
|
||||
* on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
|
||||
* this has to be done by the user.
|
||||
* It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
|
||||
* with least runtime overhead.
|
||||
*
|
||||
* @param apimsg a struct containing the function to call and its parameters
|
||||
* @param fn function to be called from TCPIP thread
|
||||
* @param apimsg argument to API function
|
||||
* @param sem semaphore to wait on
|
||||
* @return ERR_OK if the function was called, another err_t if not
|
||||
*/
|
||||
err_t
|
||||
tcpip_apimsg(struct api_msg *apimsg)
|
||||
tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
|
||||
{
|
||||
struct tcpip_msg msg;
|
||||
#ifdef LWIP_DEBUG
|
||||
/* catch functions that don't set err */
|
||||
apimsg->msg.err = ERR_VAL;
|
||||
#endif
|
||||
|
||||
if (sys_mbox_valid(&mbox)) {
|
||||
msg.type = TCPIP_MSG_API;
|
||||
msg.msg.apimsg = apimsg;
|
||||
sys_mbox_post(&mbox, &msg);
|
||||
sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);
|
||||
return apimsg->msg.err;
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
/**
|
||||
* Call the lower part of a netconn_* function
|
||||
* This function has exclusive access to lwIP core code by locking it
|
||||
* before the function is called.
|
||||
*
|
||||
* @param apimsg a struct containing the function to call and its parameters
|
||||
* @return ERR_OK (only for compatibility fo tcpip_apimsg())
|
||||
*/
|
||||
err_t
|
||||
tcpip_apimsg_lock(struct api_msg *apimsg)
|
||||
{
|
||||
#ifdef LWIP_DEBUG
|
||||
/* catch functions that don't set err */
|
||||
apimsg->msg.err = ERR_VAL;
|
||||
#endif
|
||||
|
||||
LWIP_UNUSED_ARG(sem);
|
||||
LOCK_TCPIP_CORE();
|
||||
apimsg->function(&(apimsg->msg));
|
||||
fn(apimsg);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
return apimsg->msg.err;
|
||||
return ERR_OK;
|
||||
#else /* LWIP_TCPIP_CORE_LOCKING */
|
||||
TCPIP_MSG_VAR_DECLARE(msg);
|
||||
|
||||
}
|
||||
LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
TCPIP_MSG_VAR_ALLOC(msg);
|
||||
TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
|
||||
sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
|
||||
sys_arch_sem_wait(sem, 0);
|
||||
TCPIP_MSG_VAR_FREE(msg);
|
||||
return ERR_OK;
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||
#endif /* LWIP_NETCONN */
|
||||
}
|
||||
|
||||
#if LWIP_NETIF_API
|
||||
#if !LWIP_TCPIP_CORE_LOCKING
|
||||
/**
|
||||
* Much like tcpip_apimsg, but calls the lower part of a netifapi_*
|
||||
* function.
|
||||
*
|
||||
* @param netifapimsg a struct containing the function to call and its parameters
|
||||
* @return error code given back by the function that was called
|
||||
* Synchronously calls function in TCPIP thread and waits for its completion.
|
||||
* It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
|
||||
* LWIP_NETCONN_SEM_PER_THREAD.
|
||||
* If not, a semaphore is created and destroyed on every call which is usually
|
||||
* an expensive/slow operation.
|
||||
* @param fn Function to call
|
||||
* @param call Call parameters
|
||||
* @return Return value from tcpip_api_call_fn
|
||||
*/
|
||||
err_t
|
||||
tcpip_netifapi(struct netifapi_msg* netifapimsg)
|
||||
tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
|
||||
{
|
||||
struct tcpip_msg msg;
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
err_t err;
|
||||
LOCK_TCPIP_CORE();
|
||||
err = fn(call);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
return err;
|
||||
#else /* LWIP_TCPIP_CORE_LOCKING */
|
||||
TCPIP_MSG_VAR_DECLARE(msg);
|
||||
|
||||
if (sys_mbox_valid(&mbox)) {
|
||||
err_t err = sys_sem_new(&netifapimsg->msg.sem, 0);
|
||||
#if !LWIP_NETCONN_SEM_PER_THREAD
|
||||
err_t err = sys_sem_new(&call->sem, 0);
|
||||
if (err != ERR_OK) {
|
||||
netifapimsg->msg.err = err;
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
msg.type = TCPIP_MSG_NETIFAPI;
|
||||
msg.msg.netifapimsg = netifapimsg;
|
||||
sys_mbox_post(&mbox, &msg);
|
||||
sys_sem_wait(&netifapimsg->msg.sem);
|
||||
sys_sem_free(&netifapimsg->msg.sem);
|
||||
return netifapimsg->msg.err;
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
TCPIP_MSG_VAR_ALLOC(msg);
|
||||
TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
|
||||
#if LWIP_NETCONN_SEM_PER_THREAD
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
|
||||
#else /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
|
||||
sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
|
||||
TCPIP_MSG_VAR_FREE(msg);
|
||||
|
||||
#if !LWIP_NETCONN_SEM_PER_THREAD
|
||||
sys_sem_free(&call->sem);
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
return call->err;
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
#else /* !LWIP_TCPIP_CORE_LOCKING */
|
||||
/**
|
||||
* Call the lower part of a netifapi_* function
|
||||
* This function has exclusive access to lwIP core code by locking it
|
||||
* before the function is called.
|
||||
*
|
||||
* @param netifapimsg a struct containing the function to call and its parameters
|
||||
* @return ERR_OK (only for compatibility fo tcpip_netifapi())
|
||||
*/
|
||||
err_t
|
||||
tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
|
||||
{
|
||||
LOCK_TCPIP_CORE();
|
||||
netifapimsg->function(&(netifapimsg->msg));
|
||||
UNLOCK_TCPIP_CORE();
|
||||
return netifapimsg->msg.err;
|
||||
}
|
||||
#endif /* !LWIP_TCPIP_CORE_LOCKING */
|
||||
#endif /* LWIP_NETIF_API */
|
||||
|
||||
/**
|
||||
* Allocate a structure for a static callback message and initialize it.
|
||||
@@ -405,7 +404,8 @@ tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
|
||||
* @param ctx parameter passed to function
|
||||
* @return a struct pointer to pass to tcpip_trycallback().
|
||||
*/
|
||||
struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
|
||||
struct tcpip_callback_msg*
|
||||
tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
|
||||
{
|
||||
struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
@@ -422,7 +422,8 @@ struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, voi
|
||||
*
|
||||
* @param msg the message to free
|
||||
*/
|
||||
void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
|
||||
void
|
||||
tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
|
||||
{
|
||||
memp_free(MEMP_TCPIP_MSG_API, msg);
|
||||
}
|
||||
@@ -437,13 +438,12 @@ void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
|
||||
err_t
|
||||
tcpip_trycallback(struct tcpip_callback_msg* msg)
|
||||
{
|
||||
if (!sys_mbox_valid(&mbox)) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
return sys_mbox_trypost(&mbox, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup lwip_os
|
||||
* Initialize this module:
|
||||
* - initialize all sub modules
|
||||
* - start the tcpip_thread
|
||||
|
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 724 B |
2693
ext/lwip/src/apps/httpd/httpd.c
Normal file
2693
ext/lwip/src/apps/httpd/httpd.c
Normal file
File diff suppressed because it is too large
Load Diff
1033
ext/lwip/src/apps/httpd/makefsdata/makefsdata.c
Normal file
1033
ext/lwip/src/apps/httpd/makefsdata/makefsdata.c
Normal file
File diff suppressed because it is too large
Load Diff
662
ext/lwip/src/apps/lwiperf/lwiperf.c
Normal file
662
ext/lwip/src/apps/lwiperf/lwiperf.c
Normal file
@@ -0,0 +1,662 @@
|
||||
/**
|
||||
* @file
|
||||
* lwIP iPerf server implementation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup iperf Iperf server
|
||||
* @ingroup apps
|
||||
*
|
||||
* This is a simple performance measuring server to check your bandwith using
|
||||
* iPerf2 on a PC as client.
|
||||
* It is currently a minimal implementation providing an IPv4 TCP server only.
|
||||
*
|
||||
* @todo: implement UDP mode and IPv6
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014 Simon Goldschmidt
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "lwip/apps/lwiperf.h"
|
||||
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Currently, only TCP-over-IPv4 is implemented (does iperf support IPv6 anyway?) */
|
||||
#if LWIP_IPV4 && LWIP_TCP
|
||||
|
||||
/** Specify the idle timeout (in seconds) after that the test fails */
|
||||
#ifndef LWIPERF_TCP_MAX_IDLE_SEC
|
||||
#define LWIPERF_TCP_MAX_IDLE_SEC 10U
|
||||
#endif
|
||||
#if LWIPERF_TCP_MAX_IDLE_SEC > 255
|
||||
#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
|
||||
#endif
|
||||
|
||||
/* File internal memory allocation (struct lwiperf_*): this defaults to
|
||||
the heap */
|
||||
#ifndef LWIPERF_ALLOC
|
||||
#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type))
|
||||
#define LWIPERF_FREE(type, item) mem_free(item)
|
||||
#endif
|
||||
|
||||
/** If this is 1, check that received data has the correct format */
|
||||
#ifndef LWIPERF_CHECK_RX_DATA
|
||||
#define LWIPERF_CHECK_RX_DATA 0
|
||||
#endif
|
||||
|
||||
/** This is the Iperf settings struct sent from the client */
|
||||
typedef struct _lwiperf_settings {
|
||||
#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
|
||||
#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001
|
||||
u32_t flags;
|
||||
u32_t num_threads; /* unused for now */
|
||||
u32_t remote_port;
|
||||
u32_t buffer_len; /* unused for now */
|
||||
u32_t win_band; /* TCP window / UDP rate: unused for now */
|
||||
u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
|
||||
} lwiperf_settings_t;
|
||||
|
||||
/** Basic connection handle */
|
||||
struct _lwiperf_state_base;
|
||||
typedef struct _lwiperf_state_base lwiperf_state_base_t;
|
||||
struct _lwiperf_state_base {
|
||||
/* 1=tcp, 0=udp */
|
||||
u8_t tcp;
|
||||
/* 1=server, 0=client */
|
||||
u8_t server;
|
||||
lwiperf_state_base_t* next;
|
||||
lwiperf_state_base_t* related_server_state;
|
||||
};
|
||||
|
||||
/** Connection handle for a TCP iperf session */
|
||||
typedef struct _lwiperf_state_tcp {
|
||||
lwiperf_state_base_t base;
|
||||
struct tcp_pcb* server_pcb;
|
||||
struct tcp_pcb* conn_pcb;
|
||||
u32_t time_started;
|
||||
lwiperf_report_fn report_fn;
|
||||
void* report_arg;
|
||||
u8_t poll_count;
|
||||
u8_t next_num;
|
||||
u32_t bytes_transferred;
|
||||
lwiperf_settings_t settings;
|
||||
u8_t have_settings_buf;
|
||||
} lwiperf_state_tcp_t;
|
||||
|
||||
/** List of active iperf sessions */
|
||||
static lwiperf_state_base_t* lwiperf_all_connections;
|
||||
/** A const buffer to send from: we want to measure sending, not copying! */
|
||||
static const u8_t lwiperf_txbuf_const[1600] = {
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
};
|
||||
|
||||
static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
|
||||
static void lwiperf_tcp_err(void *arg, err_t err);
|
||||
|
||||
/** Add an iperf session to the 'active' list */
|
||||
static void
|
||||
lwiperf_list_add(lwiperf_state_base_t* item)
|
||||
{
|
||||
if (lwiperf_all_connections == NULL) {
|
||||
lwiperf_all_connections = item;
|
||||
} else {
|
||||
item = lwiperf_all_connections;
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove an iperf session from the 'active' list */
|
||||
static void
|
||||
lwiperf_list_remove(lwiperf_state_base_t* item)
|
||||
{
|
||||
lwiperf_state_base_t* prev = NULL;
|
||||
lwiperf_state_base_t* iter;
|
||||
for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
|
||||
if (iter == item) {
|
||||
if (prev == NULL) {
|
||||
lwiperf_all_connections = iter->next;
|
||||
} else {
|
||||
prev->next = item;
|
||||
}
|
||||
/* @debug: ensure this item is listed only once */
|
||||
for (iter = iter->next; iter != NULL; iter = iter->next) {
|
||||
LWIP_ASSERT("duplicate entry", iter != item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Call the report function of an iperf tcp session */
|
||||
static void
|
||||
lwip_tcp_conn_report(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
|
||||
{
|
||||
if ((conn != NULL) && (conn->report_fn != NULL)) {
|
||||
u32_t now, duration_ms, bandwidth_kbitpsec;
|
||||
now = sys_now();
|
||||
duration_ms = now - conn->time_started;
|
||||
if (duration_ms == 0) {
|
||||
bandwidth_kbitpsec = 0;
|
||||
} else {
|
||||
bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
|
||||
}
|
||||
conn->report_fn(conn->report_arg, report_type,
|
||||
&conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
|
||||
&conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
|
||||
conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
|
||||
}
|
||||
}
|
||||
|
||||
/** Close an iperf tcp session */
|
||||
static void
|
||||
lwiperf_tcp_close(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
lwip_tcp_conn_report(conn, report_type);
|
||||
lwiperf_list_remove(&conn->base);
|
||||
if (conn->conn_pcb != NULL) {
|
||||
tcp_arg(conn->conn_pcb, NULL);
|
||||
tcp_poll(conn->conn_pcb, NULL, 0);
|
||||
tcp_sent(conn->conn_pcb, NULL);
|
||||
tcp_recv(conn->conn_pcb, NULL);
|
||||
tcp_err(conn->conn_pcb, NULL);
|
||||
err = tcp_close(conn->conn_pcb);
|
||||
if (err != ERR_OK) {
|
||||
/* don't want to wait for free memory here... */
|
||||
tcp_abort(conn->conn_pcb);
|
||||
}
|
||||
} else {
|
||||
/* no conn pcb, this is the server pcb */
|
||||
err = tcp_close(conn->server_pcb);
|
||||
LWIP_ASSERT("error", err != ERR_OK);
|
||||
}
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, conn);
|
||||
}
|
||||
|
||||
/** Try to send more data on an iperf tcp session */
|
||||
static err_t
|
||||
lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn)
|
||||
{
|
||||
int send_more;
|
||||
err_t err;
|
||||
u16_t txlen;
|
||||
u16_t txlen_max;
|
||||
void* txptr;
|
||||
u8_t apiflags;
|
||||
|
||||
LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
|
||||
|
||||
do {
|
||||
send_more = 0;
|
||||
if (conn->settings.amount & PP_HTONL(0x80000000)) {
|
||||
/* this session is time-limited */
|
||||
u32_t now = sys_now();
|
||||
u32_t diff_ms = now - conn->time_started;
|
||||
u32_t time = (u32_t)-(s32_t)htonl(conn->settings.amount);
|
||||
u32_t time_ms = time * 10;
|
||||
if (diff_ms >= time_ms) {
|
||||
/* time specified by the client is over -> close the connection */
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
|
||||
return ERR_OK;
|
||||
}
|
||||
} else {
|
||||
/* this session is byte-limited */
|
||||
u32_t amount_bytes = htonl(conn->settings.amount);
|
||||
/* @todo: this can send up to 1*MSS more than requested... */
|
||||
if (amount_bytes >= conn->bytes_transferred) {
|
||||
/* all requested bytes transferred -> close the connection */
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->bytes_transferred < 24) {
|
||||
/* transmit the settings a first time */
|
||||
txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred];
|
||||
txlen_max = (u16_t)(24 - conn->bytes_transferred);
|
||||
apiflags = TCP_WRITE_FLAG_COPY;
|
||||
} else if (conn->bytes_transferred < 48) {
|
||||
/* transmit the settings a second time */
|
||||
txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred - 24];
|
||||
txlen_max = (u16_t)(48 - conn->bytes_transferred);
|
||||
apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
|
||||
send_more = 1;
|
||||
} else {
|
||||
/* transmit data */
|
||||
/* @todo: every x bytes, transmit the settings again */
|
||||
txptr = (void*)(size_t)&lwiperf_txbuf_const[conn->bytes_transferred % 10];
|
||||
txlen_max = TCP_MSS;
|
||||
if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
|
||||
txlen_max = TCP_MSS - 24;
|
||||
}
|
||||
apiflags = 0; /* no copying needed */
|
||||
send_more = 1;
|
||||
}
|
||||
txlen = txlen_max;
|
||||
do {
|
||||
err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
|
||||
if (err == ERR_MEM) {
|
||||
txlen /= 2;
|
||||
}
|
||||
} while ((err == ERR_MEM) && (txlen >= (TCP_MSS/2)));
|
||||
|
||||
if (err == ERR_OK) {
|
||||
conn->bytes_transferred += txlen;
|
||||
} else {
|
||||
send_more = 0;
|
||||
}
|
||||
} while(send_more);
|
||||
|
||||
tcp_output(conn->conn_pcb);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** TCP sent callback, try to send more data */
|
||||
static err_t
|
||||
lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
/* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
|
||||
LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
conn->poll_count = 0;
|
||||
|
||||
return lwiperf_tcp_client_send_more(conn);
|
||||
}
|
||||
|
||||
/** TCP connected callback (active connection), send data now */
|
||||
static err_t
|
||||
lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
if (err != ERR_OK) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
|
||||
return ERR_OK;
|
||||
}
|
||||
conn->poll_count = 0;
|
||||
conn->time_started = sys_now();
|
||||
return lwiperf_tcp_client_send_more(conn);
|
||||
}
|
||||
|
||||
/** Start TCP connection back to the client (either parallel or after the
|
||||
* receive test has finished.
|
||||
*/
|
||||
static err_t
|
||||
lwiperf_tx_start(lwiperf_state_tcp_t* conn)
|
||||
{
|
||||
err_t err;
|
||||
lwiperf_state_tcp_t* client_conn;
|
||||
struct tcp_pcb* newpcb;
|
||||
ip_addr_t remote_addr;
|
||||
u16_t remote_port;
|
||||
|
||||
client_conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
|
||||
if (client_conn == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
newpcb = tcp_new();
|
||||
if (newpcb == NULL) {
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
memcpy(client_conn, conn, sizeof(lwiperf_state_tcp_t));
|
||||
client_conn->base.server = 0;
|
||||
client_conn->server_pcb = NULL;
|
||||
client_conn->conn_pcb = newpcb;
|
||||
client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
|
||||
client_conn->poll_count = 0;
|
||||
client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
|
||||
client_conn->bytes_transferred = 0;
|
||||
client_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
|
||||
|
||||
tcp_arg(newpcb, client_conn);
|
||||
tcp_sent(newpcb, lwiperf_tcp_client_sent);
|
||||
tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
|
||||
tcp_err(newpcb, lwiperf_tcp_err);
|
||||
|
||||
ip_addr_copy(remote_addr, conn->conn_pcb->remote_ip);
|
||||
remote_port = (u16_t)htonl(client_conn->settings.remote_port);
|
||||
|
||||
err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
|
||||
if (err != ERR_OK) {
|
||||
lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
|
||||
return err;
|
||||
}
|
||||
lwiperf_list_add(&client_conn->base);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** Receive data on an iperf tcp session */
|
||||
static err_t
|
||||
lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
|
||||
{
|
||||
u16_t tot_len;
|
||||
u32_t packet_idx;
|
||||
struct pbuf* q;
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
|
||||
LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
|
||||
if (err != ERR_OK) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
|
||||
return ERR_OK;
|
||||
}
|
||||
if (p == NULL) {
|
||||
/* connection closed -> test done */
|
||||
if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
|
||||
PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
|
||||
/* client requested transmission after end of test */
|
||||
lwiperf_tx_start(conn);
|
||||
}
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
|
||||
return ERR_OK;
|
||||
}
|
||||
tot_len = p->tot_len;
|
||||
|
||||
conn->poll_count = 0;
|
||||
|
||||
if ((!conn->have_settings_buf) || ((conn->bytes_transferred -24) % (1024*128) == 0)) {
|
||||
/* wait for 24-byte header */
|
||||
if (p->tot_len < sizeof(lwiperf_settings_t)) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
if (!conn->have_settings_buf) {
|
||||
if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
conn->have_settings_buf = 1;
|
||||
if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
|
||||
PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) {
|
||||
/* client requested parallel transmission test */
|
||||
err_t err2 = lwiperf_tx_start(conn);
|
||||
if (err2 != ERR_OK) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
|
||||
pbuf_free(p);
|
||||
return err2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
}
|
||||
conn->bytes_transferred += sizeof(lwiperf_settings_t);
|
||||
if (conn->bytes_transferred <= 24) {
|
||||
conn->time_started = sys_now();
|
||||
tcp_recved(tpcb, p->tot_len);
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
conn->next_num = 4; /* 24 bytes received... */
|
||||
err = pbuf_header(p, -24);
|
||||
LWIP_ASSERT("pbuf_header failed", err == ERR_OK);
|
||||
}
|
||||
|
||||
packet_idx = 0;
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
#if LWIPERF_CHECK_RX_DATA
|
||||
const u8_t* payload = (const u8_t*)q->payload;
|
||||
u16_t i;
|
||||
for (i = 0; i < q->len; i++) {
|
||||
u8_t val = payload[i];
|
||||
u8_t num = val - '0';
|
||||
if (num == conn->next_num) {
|
||||
conn->next_num++;
|
||||
if (conn->next_num == 10) {
|
||||
conn->next_num = 0;
|
||||
}
|
||||
} else {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
}
|
||||
packet_idx += i;
|
||||
#else
|
||||
packet_idx += q->len;
|
||||
#endif
|
||||
}
|
||||
LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
|
||||
conn->bytes_transferred += packet_idx;
|
||||
tcp_recved(tpcb, tot_len);
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** Error callback, iperf tcp session aborted */
|
||||
static void
|
||||
lwiperf_tcp_err(void *arg, err_t err)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
LWIP_UNUSED_ARG(err);
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
|
||||
}
|
||||
|
||||
/** TCP poll callback, try to send more data */
|
||||
static err_t
|
||||
lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
|
||||
return ERR_OK; /* lwiperf_tcp_close frees conn */
|
||||
}
|
||||
|
||||
if (!conn->base.server) {
|
||||
lwiperf_tcp_client_send_more(conn);
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** This is called when a new client connects for an iperf tcp session */
|
||||
static err_t
|
||||
lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
||||
{
|
||||
lwiperf_state_tcp_t *s, *conn;
|
||||
if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
s = (lwiperf_state_tcp_t*)arg;
|
||||
conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
|
||||
if (conn == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
memset(conn, 0, sizeof(lwiperf_state_tcp_t));
|
||||
conn->base.tcp = 1;
|
||||
conn->base.server = 1;
|
||||
conn->base.related_server_state = &s->base;
|
||||
conn->server_pcb = s->server_pcb;
|
||||
conn->conn_pcb = newpcb;
|
||||
conn->time_started = sys_now();
|
||||
conn->report_fn = s->report_fn;
|
||||
conn->report_arg = s->report_arg;
|
||||
|
||||
/* setup the tcp rx connection */
|
||||
tcp_arg(newpcb, conn);
|
||||
tcp_recv(newpcb, lwiperf_tcp_recv);
|
||||
tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
|
||||
tcp_err(conn->conn_pcb, lwiperf_tcp_err);
|
||||
|
||||
lwiperf_list_add(&conn->base);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iperf
|
||||
* Start a TCP iperf server on the default TCP port (5001) and listen for
|
||||
* incoming connections from iperf clients.
|
||||
*
|
||||
* @returns a connection handle that can be used to abort the server
|
||||
* by calling @ref lwiperf_abort()
|
||||
*/
|
||||
void*
|
||||
lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg)
|
||||
{
|
||||
return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
|
||||
report_fn, report_arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iperf
|
||||
* Start a TCP iperf server on a specific IP address and port and listen for
|
||||
* incoming connections from iperf clients.
|
||||
*
|
||||
* @returns a connection handle that can be used to abort the server
|
||||
* by calling @ref lwiperf_abort()
|
||||
*/
|
||||
void*
|
||||
lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port,
|
||||
lwiperf_report_fn report_fn, void* report_arg)
|
||||
{
|
||||
err_t err;
|
||||
struct tcp_pcb* pcb;
|
||||
lwiperf_state_tcp_t* s;
|
||||
|
||||
if (local_addr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(s, 0, sizeof(lwiperf_state_tcp_t));
|
||||
s->base.tcp = 1;
|
||||
s->base.server = 1;
|
||||
s->report_fn = report_fn;
|
||||
s->report_arg = report_arg;
|
||||
|
||||
pcb = tcp_new();
|
||||
if (pcb != NULL) {
|
||||
err = tcp_bind(pcb, local_addr, local_port);
|
||||
if (err == ERR_OK) {
|
||||
s->server_pcb = tcp_listen_with_backlog(pcb, 1);
|
||||
}
|
||||
}
|
||||
if (s->server_pcb == NULL) {
|
||||
if (pcb != NULL) {
|
||||
tcp_close(pcb);
|
||||
}
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, s);
|
||||
return NULL;
|
||||
}
|
||||
pcb = NULL;
|
||||
|
||||
tcp_arg(s->server_pcb, s);
|
||||
tcp_accept(s->server_pcb, lwiperf_tcp_accept);
|
||||
|
||||
lwiperf_list_add(&s->base);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iperf
|
||||
* Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
|
||||
*/
|
||||
void
|
||||
lwiperf_abort(void* lwiperf_session)
|
||||
{
|
||||
lwiperf_state_base_t* i, *dealloc, *last = NULL;
|
||||
|
||||
for (i = lwiperf_all_connections; i != NULL; ) {
|
||||
if ((i == lwiperf_session) || (i->related_server_state == lwiperf_session)) {
|
||||
dealloc = i;
|
||||
i = i->next;
|
||||
if (last != NULL) {
|
||||
last->next = i;
|
||||
}
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
|
||||
} else {
|
||||
last = i;
|
||||
i = i->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_TCP */
|
||||
366
ext/lwip/src/apps/netbiosns/netbiosns.c
Normal file
366
ext/lwip/src/apps/netbiosns/netbiosns.c
Normal file
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* @file
|
||||
* NetBIOS name service responder
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup netbiosns NETBIOS responder
|
||||
* @ingroup apps
|
||||
*
|
||||
* This is an example implementation of a NetBIOS name server.
|
||||
* It responds to name queries for a configurable name.
|
||||
* Name resolving is not supported.
|
||||
*
|
||||
* Note that the device doesn't broadcast it's own name so can't
|
||||
* detect duplicate names!
|
||||
*/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/apps/netbiosns.h"
|
||||
|
||||
#if LWIP_IPV4 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/** default port number for "NetBIOS Name service */
|
||||
#define NETBIOS_PORT 137
|
||||
|
||||
/** size of a NetBIOS name */
|
||||
#define NETBIOS_NAME_LEN 16
|
||||
|
||||
/** The Time-To-Live for NetBIOS name responds (in seconds)
|
||||
* Default is 300000 seconds (3 days, 11 hours, 20 minutes) */
|
||||
#define NETBIOS_NAME_TTL 300000u
|
||||
|
||||
/** NetBIOS header flags */
|
||||
#define NETB_HFLAG_RESPONSE 0x8000U
|
||||
#define NETB_HFLAG_OPCODE 0x7800U
|
||||
#define NETB_HFLAG_OPCODE_NAME_QUERY 0x0000U
|
||||
#define NETB_HFLAG_AUTHORATIVE 0x0400U
|
||||
#define NETB_HFLAG_TRUNCATED 0x0200U
|
||||
#define NETB_HFLAG_RECURS_DESIRED 0x0100U
|
||||
#define NETB_HFLAG_RECURS_AVAILABLE 0x0080U
|
||||
#define NETB_HFLAG_BROADCAST 0x0010U
|
||||
#define NETB_HFLAG_REPLYCODE 0x0008U
|
||||
#define NETB_HFLAG_REPLYCODE_NOERROR 0x0000U
|
||||
|
||||
/** NetBIOS name flags */
|
||||
#define NETB_NFLAG_UNIQUE 0x8000U
|
||||
#define NETB_NFLAG_NODETYPE 0x6000U
|
||||
#define NETB_NFLAG_NODETYPE_HNODE 0x6000U
|
||||
#define NETB_NFLAG_NODETYPE_MNODE 0x4000U
|
||||
#define NETB_NFLAG_NODETYPE_PNODE 0x2000U
|
||||
#define NETB_NFLAG_NODETYPE_BNODE 0x0000U
|
||||
|
||||
/** NetBIOS message header */
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct netbios_hdr {
|
||||
PACK_STRUCT_FIELD(u16_t trans_id);
|
||||
PACK_STRUCT_FIELD(u16_t flags);
|
||||
PACK_STRUCT_FIELD(u16_t questions);
|
||||
PACK_STRUCT_FIELD(u16_t answerRRs);
|
||||
PACK_STRUCT_FIELD(u16_t authorityRRs);
|
||||
PACK_STRUCT_FIELD(u16_t additionalRRs);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
/** NetBIOS message name part */
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct netbios_name_hdr {
|
||||
PACK_STRUCT_FLD_8(u8_t nametype);
|
||||
PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN*2)+1]);
|
||||
PACK_STRUCT_FIELD(u16_t type);
|
||||
PACK_STRUCT_FIELD(u16_t cls);
|
||||
PACK_STRUCT_FIELD(u32_t ttl);
|
||||
PACK_STRUCT_FIELD(u16_t datalen);
|
||||
PACK_STRUCT_FIELD(u16_t flags);
|
||||
PACK_STRUCT_FLD_S(ip4_addr_p_t addr);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
/** NetBIOS message */
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct netbios_resp
|
||||
{
|
||||
struct netbios_hdr resp_hdr;
|
||||
struct netbios_name_hdr resp_name;
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#ifdef NETBIOS_LWIP_NAME
|
||||
#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME
|
||||
#else
|
||||
static char netbiosns_local_name[NETBIOS_NAME_LEN];
|
||||
#define NETBIOS_LOCAL_NAME netbiosns_local_name
|
||||
#endif
|
||||
|
||||
struct udp_pcb *netbiosns_pcb;
|
||||
|
||||
/** Decode a NetBIOS name (from packet to string) */
|
||||
static int
|
||||
netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len)
|
||||
{
|
||||
char *pname;
|
||||
char cname;
|
||||
char cnbname;
|
||||
int idx = 0;
|
||||
|
||||
LWIP_UNUSED_ARG(name_dec_len);
|
||||
|
||||
/* Start decoding netbios name. */
|
||||
pname = name_enc;
|
||||
for (;;) {
|
||||
/* Every two characters of the first level-encoded name
|
||||
* turn into one character in the decoded name. */
|
||||
cname = *pname;
|
||||
if (cname == '\0')
|
||||
break; /* no more characters */
|
||||
if (cname == '.')
|
||||
break; /* scope ID follows */
|
||||
if (cname < 'A' || cname > 'Z') {
|
||||
/* Not legal. */
|
||||
return -1;
|
||||
}
|
||||
cname -= 'A';
|
||||
cnbname = cname << 4;
|
||||
pname++;
|
||||
|
||||
cname = *pname;
|
||||
if (cname == '\0' || cname == '.') {
|
||||
/* No more characters in the name - but we're in
|
||||
* the middle of a pair. Not legal. */
|
||||
return -1;
|
||||
}
|
||||
if (cname < 'A' || cname > 'Z') {
|
||||
/* Not legal. */
|
||||
return -1;
|
||||
}
|
||||
cname -= 'A';
|
||||
cnbname |= cname;
|
||||
pname++;
|
||||
|
||||
/* Do we have room to store the character? */
|
||||
if (idx < NETBIOS_NAME_LEN) {
|
||||
/* Yes - store the character. */
|
||||
name_dec[idx++] = (cnbname!=' '?cnbname:'\0');
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* function currently unused */
|
||||
/** Encode a NetBIOS name (from string to packet) - currently unused because
|
||||
we don't ask for names. */
|
||||
static int
|
||||
netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len)
|
||||
{
|
||||
char *pname;
|
||||
char cname;
|
||||
unsigned char ucname;
|
||||
int idx = 0;
|
||||
|
||||
/* Start encoding netbios name. */
|
||||
pname = name_enc;
|
||||
|
||||
for (;;) {
|
||||
/* Every two characters of the first level-encoded name
|
||||
* turn into one character in the decoded name. */
|
||||
cname = *pname;
|
||||
if (cname == '\0')
|
||||
break; /* no more characters */
|
||||
if (cname == '.')
|
||||
break; /* scope ID follows */
|
||||
if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) {
|
||||
/* Not legal. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Do we have room to store the character? */
|
||||
if (idx >= name_dec_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yes - store the character. */
|
||||
ucname = cname;
|
||||
name_dec[idx++] = ('A'+((ucname>>4) & 0x0F));
|
||||
name_dec[idx++] = ('A'+( ucname & 0x0F));
|
||||
pname++;
|
||||
}
|
||||
|
||||
/* Fill with "space" coding */
|
||||
for (;idx < name_dec_len - 1;) {
|
||||
name_dec[idx++] = 'C';
|
||||
name_dec[idx++] = 'A';
|
||||
}
|
||||
|
||||
/* Terminate string */
|
||||
name_dec[idx] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
/** NetBIOS Name service recv callback */
|
||||
static void
|
||||
netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
/* if packet is valid */
|
||||
if (p != NULL) {
|
||||
char netbios_name[NETBIOS_NAME_LEN+1];
|
||||
struct netbios_hdr* netbios_hdr = (struct netbios_hdr*)p->payload;
|
||||
struct netbios_name_hdr* netbios_name_hdr = (struct netbios_name_hdr*)(netbios_hdr+1);
|
||||
|
||||
/* we only answer if we got a default interface */
|
||||
if (netif_default != NULL) {
|
||||
/* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */
|
||||
/* if the packet is a NetBIOS name query question */
|
||||
if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
|
||||
((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) &&
|
||||
(netbios_hdr->questions == PP_NTOHS(1))) {
|
||||
/* decode the NetBIOS name */
|
||||
netbiosns_name_decode((char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
|
||||
/* if the packet is for us */
|
||||
if (NETBIOS_STRCMP(netbios_name, NETBIOS_LOCAL_NAME) == 0) {
|
||||
struct pbuf *q;
|
||||
struct netbios_resp *resp;
|
||||
|
||||
q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM);
|
||||
if (q != NULL) {
|
||||
resp = (struct netbios_resp*)q->payload;
|
||||
|
||||
/* prepare NetBIOS header response */
|
||||
resp->resp_hdr.trans_id = netbios_hdr->trans_id;
|
||||
resp->resp_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE |
|
||||
NETB_HFLAG_OPCODE_NAME_QUERY |
|
||||
NETB_HFLAG_AUTHORATIVE |
|
||||
NETB_HFLAG_RECURS_DESIRED);
|
||||
resp->resp_hdr.questions = 0;
|
||||
resp->resp_hdr.answerRRs = PP_HTONS(1);
|
||||
resp->resp_hdr.authorityRRs = 0;
|
||||
resp->resp_hdr.additionalRRs = 0;
|
||||
|
||||
/* prepare NetBIOS header datas */
|
||||
MEMCPY( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));
|
||||
resp->resp_name.nametype = netbios_name_hdr->nametype;
|
||||
resp->resp_name.type = netbios_name_hdr->type;
|
||||
resp->resp_name.cls = netbios_name_hdr->cls;
|
||||
resp->resp_name.ttl = PP_HTONL(NETBIOS_NAME_TTL);
|
||||
resp->resp_name.datalen = PP_HTONS(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr));
|
||||
resp->resp_name.flags = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE);
|
||||
ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default));
|
||||
|
||||
/* send the NetBIOS response */
|
||||
udp_sendto(upcb, q, addr, port);
|
||||
|
||||
/* free the "reference" pbuf */
|
||||
pbuf_free(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* free the pbuf */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbiosns
|
||||
* Init netbios responder
|
||||
*/
|
||||
void
|
||||
netbiosns_init(void)
|
||||
{
|
||||
#ifdef NETBIOS_LWIP_NAME
|
||||
LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN);
|
||||
#endif
|
||||
|
||||
netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
|
||||
if (netbiosns_pcb != NULL) {
|
||||
/* we have to be allowed to send broadcast packets! */
|
||||
netbiosns_pcb->so_options |= SOF_BROADCAST;
|
||||
udp_bind(netbiosns_pcb, IP_ANY_TYPE, NETBIOS_PORT);
|
||||
udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NETBIOS_LWIP_NAME
|
||||
/**
|
||||
* @ingroup netbiosns
|
||||
* Set netbios name. ATTENTION: the hostname must be less than 15 characters!
|
||||
*/
|
||||
void
|
||||
netbiosns_set_name(const char* hostname)
|
||||
{
|
||||
size_t copy_len = strlen(hostname);
|
||||
LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN);
|
||||
if (copy_len >= NETBIOS_NAME_LEN) {
|
||||
copy_len = NETBIOS_NAME_LEN - 1;
|
||||
}
|
||||
memcpy(netbiosns_local_name, hostname, copy_len + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @ingroup netbiosns
|
||||
* Stop netbios responder
|
||||
*/
|
||||
void
|
||||
netbiosns_stop(void)
|
||||
{
|
||||
if (netbiosns_pcb != NULL) {
|
||||
udp_remove(netbiosns_pcb);
|
||||
netbiosns_pcb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_UDP */
|
||||
38
ext/lwip/src/apps/snmp/README
Normal file
38
ext/lwip/src/apps/snmp/README
Normal file
@@ -0,0 +1,38 @@
|
||||
lwIP SNMPv2c agent
|
||||
==================
|
||||
|
||||
Based on SNMP stack written by Christiaan Simons <christiaan.simons@axon.tv>
|
||||
|
||||
Rewritten by Martin Hentschel <info@cl-soft.de> and
|
||||
Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
|
||||
Features:
|
||||
- SNMPv2c support.
|
||||
- Low RAM usage - no memory pools, stack only.
|
||||
- MIB2 implementation is separated from SNMP stack.
|
||||
- Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
|
||||
- Simple and generic API for MIB implementation.
|
||||
- Comfortable node types and helper functions for scalar arrays and tables.
|
||||
- Counter64, bit and truthvalue datatype support.
|
||||
- Callbacks for SNMP writes e.g. to implement persistency.
|
||||
- Runs on two APIs: RAW and netconn.
|
||||
- Async API is gone - the stack now supports netconn API instead,
|
||||
so blocking operations can be done in MIB calls.
|
||||
SNMP runs in a worker thread when netconn API is used.
|
||||
- Simplified thread sync support for MIBs - useful when MIBs
|
||||
need to access variables shared with other threads where no locking is
|
||||
possible. Used in MIB2 to access lwIP stats from lwIP thread.
|
||||
|
||||
MIB compiler (code generator):
|
||||
- Written in C#. MIB viewer used Windows Forms.
|
||||
- Developed on Windows with Visual Studio 2010.
|
||||
- Can be compiled and used under Linux with http://www.monodevelop.com/.
|
||||
- Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
|
||||
(https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
|
||||
- MIB parser, C file generation framework and LWIP code generation are cleanly
|
||||
separated, which means the code may be useful as a base for code generation
|
||||
of other SNMP agents.
|
||||
|
||||
Notes:
|
||||
- Stack and MIB compiler were used to implement a Profinet device.
|
||||
Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
|
||||
749
ext/lwip/src/apps/snmp/snmp_asn1.c
Normal file
749
ext/lwip/src/apps/snmp/snmp_asn1.c
Normal file
@@ -0,0 +1,749 @@
|
||||
/**
|
||||
* @file
|
||||
* Abstract Syntax Notation One (ISO 8824, 8825) encoding
|
||||
*
|
||||
* @todo not optimised (yet), favor correctness over speed, favor speed over size
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Christiaan Simons <christiaan.simons@axon.tv>
|
||||
* Martin Hentschel <info@cl-soft.de>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "snmp_asn1.h"
|
||||
|
||||
#define PBUF_OP_EXEC(code) \
|
||||
if ((code) != ERR_OK) { \
|
||||
return ERR_BUF; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a TLV into a pbuf stream.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param tlv TLV to encode
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*/
|
||||
err_t
|
||||
snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
|
||||
{
|
||||
u8_t data;
|
||||
u8_t length_bytes_required;
|
||||
|
||||
/* write type */
|
||||
if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
|
||||
/* extended format is not used by SNMP so we do not accept those values */
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (tlv->type_len != 0) {
|
||||
/* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
|
||||
tlv->type_len = 1;
|
||||
|
||||
/* write length */
|
||||
if (tlv->value_len <= 127) {
|
||||
length_bytes_required = 1;
|
||||
} else if (tlv->value_len <= 255) {
|
||||
length_bytes_required = 2;
|
||||
} else {
|
||||
length_bytes_required = 3;
|
||||
}
|
||||
|
||||
/* check for forced min length */
|
||||
if (tlv->length_len > 0) {
|
||||
if (tlv->length_len < length_bytes_required) {
|
||||
/* unable to code requested length in requested number of bytes */
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
length_bytes_required = tlv->length_len;
|
||||
} else {
|
||||
tlv->length_len = length_bytes_required;
|
||||
}
|
||||
|
||||
if (length_bytes_required > 1) {
|
||||
/* multi byte representation required */
|
||||
length_bytes_required--;
|
||||
data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
|
||||
|
||||
while (length_bytes_required > 1) {
|
||||
if (length_bytes_required == 2) {
|
||||
/* append high byte */
|
||||
data = (u8_t)(tlv->value_len >> 8);
|
||||
} else {
|
||||
/* append leading 0x00 */
|
||||
data = 0x00;
|
||||
}
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
|
||||
length_bytes_required--;
|
||||
}
|
||||
}
|
||||
|
||||
/* append low byte */
|
||||
data = (u8_t)(tlv->value_len & 0xFF);
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param raw_len raw data length
|
||||
* @param raw points raw data
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
|
||||
{
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
|
||||
* @param value is the host order u32_t value to be encoded
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*
|
||||
* @see snmp_asn1_enc_u32t_cnt()
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
|
||||
{
|
||||
if (octets_needed > 5) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (octets_needed == 5) {
|
||||
/* not enough bits in 'value' add leading 0x00 */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
|
||||
octets_needed--;
|
||||
}
|
||||
|
||||
while (octets_needed > 1) {
|
||||
octets_needed--;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
|
||||
}
|
||||
|
||||
/* (only) one least significant octet */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
|
||||
* @param value is the host order u32_t value to be encoded
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*
|
||||
* @see snmp_asn1_enc_u64t_cnt()
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
|
||||
{
|
||||
if (octets_needed > 9) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (octets_needed == 9) {
|
||||
/* not enough bits in 'value' add leading 0x00 */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
|
||||
octets_needed--;
|
||||
}
|
||||
|
||||
while (octets_needed > 4) {
|
||||
octets_needed--;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
|
||||
}
|
||||
|
||||
/* skip to low u32 */
|
||||
value++;
|
||||
|
||||
while (octets_needed > 1) {
|
||||
octets_needed--;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
|
||||
}
|
||||
|
||||
/* always write at least one octet (also in case of value == 0) */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes s32_t integer into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
|
||||
* @param value is the host order s32_t value to be encoded
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*
|
||||
* @see snmp_asn1_enc_s32t_cnt()
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
|
||||
{
|
||||
while (octets_needed > 1) {
|
||||
octets_needed--;
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
|
||||
}
|
||||
|
||||
/* (only) one least significant octet */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes object identifier into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param oid points to object identifier array
|
||||
* @param oid_len object identifier array length
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
|
||||
{
|
||||
if (oid_len > 1) {
|
||||
/* write compressed first two sub id's */
|
||||
u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
|
||||
oid_len -= 2;
|
||||
oid += 2;
|
||||
} else {
|
||||
/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
|
||||
/* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
while (oid_len > 0) {
|
||||
u32_t sub_id;
|
||||
u8_t shift, tail;
|
||||
|
||||
oid_len--;
|
||||
sub_id = *oid;
|
||||
tail = 0;
|
||||
shift = 28;
|
||||
while (shift > 0) {
|
||||
u8_t code;
|
||||
|
||||
code = (u8_t)(sub_id >> shift);
|
||||
if ((code != 0) || (tail != 0)) {
|
||||
tail = 1;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
|
||||
}
|
||||
shift -= 7;
|
||||
}
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
|
||||
|
||||
/* proceed to next sub-identifier */
|
||||
oid++;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for length.
|
||||
*
|
||||
* @param length
|
||||
* @param octets_needed points to the return value
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
|
||||
{
|
||||
if (length < 0x80U) {
|
||||
*octets_needed = 1;
|
||||
} else if (length < 0x100U) {
|
||||
*octets_needed = 2;
|
||||
} else {
|
||||
*octets_needed = 3;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an u32_t.
|
||||
*
|
||||
* @param value
|
||||
* @param octets_needed points to the return value
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
|
||||
{
|
||||
if (value < 0x80UL) {
|
||||
*octets_needed = 1;
|
||||
} else if (value < 0x8000UL) {
|
||||
*octets_needed = 2;
|
||||
} else if (value < 0x800000UL) {
|
||||
*octets_needed = 3;
|
||||
} else if (value < 0x80000000UL) {
|
||||
*octets_needed = 4;
|
||||
} else {
|
||||
*octets_needed = 5;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an u64_t.
|
||||
*
|
||||
* @param value
|
||||
* @param octets_needed points to the return value
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
|
||||
{
|
||||
/* check if high u32 is 0 */
|
||||
if (*value == 0x00) {
|
||||
/* only low u32 is important */
|
||||
value++;
|
||||
snmp_asn1_enc_u32t_cnt(*value, octets_needed);
|
||||
} else {
|
||||
/* low u32 does not matter for length determination */
|
||||
snmp_asn1_enc_u32t_cnt(*value, octets_needed);
|
||||
*octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an s32_t.
|
||||
*
|
||||
* @param value
|
||||
* @param octets_needed points to the return value
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed.
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
|
||||
{
|
||||
if (value < 0) {
|
||||
value = ~value;
|
||||
}
|
||||
if (value < 0x80L) {
|
||||
*octets_needed = 1;
|
||||
} else if (value < 0x8000L) {
|
||||
*octets_needed = 2;
|
||||
} else if (value < 0x800000L) {
|
||||
*octets_needed = 3;
|
||||
} else {
|
||||
*octets_needed = 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an object identifier.
|
||||
*
|
||||
* @param oid points to object identifier array
|
||||
* @param oid_len object identifier array length
|
||||
* @param octets_needed points to the return value
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
|
||||
{
|
||||
u32_t sub_id;
|
||||
|
||||
*octets_needed = 0;
|
||||
if (oid_len > 1) {
|
||||
/* compressed prefix in one octet */
|
||||
(*octets_needed)++;
|
||||
oid_len -= 2;
|
||||
oid += 2;
|
||||
}
|
||||
while (oid_len > 0) {
|
||||
oid_len--;
|
||||
sub_id = *oid;
|
||||
|
||||
sub_id >>= 7;
|
||||
(*octets_needed)++;
|
||||
while (sub_id > 0) {
|
||||
sub_id >>= 7;
|
||||
(*octets_needed)++;
|
||||
}
|
||||
oid++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a TLV from a pbuf stream.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param tlv returns decoded TLV
|
||||
* @return ERR_OK if successful, ERR_VAL if we can't decode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
|
||||
{
|
||||
u8_t data;
|
||||
|
||||
/* decode type first */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
tlv->type = data;
|
||||
|
||||
if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
|
||||
/* extended format is not used by SNMP so we do not accept those values */
|
||||
return ERR_VAL;
|
||||
}
|
||||
tlv->type_len = 1;
|
||||
|
||||
/* now, decode length */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
if (data < 0x80) { /* short form */
|
||||
tlv->length_len = 1;
|
||||
tlv->value_len = data;
|
||||
} else if (data > 0x80) { /* long form */
|
||||
u8_t length_bytes = data - 0x80;
|
||||
tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
|
||||
tlv->value_len = 0;
|
||||
|
||||
while (length_bytes > 0) {
|
||||
/* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
|
||||
if (tlv->value_len > 0xFF) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
tlv->value_len <<= 8;
|
||||
tlv->value_len |= data;
|
||||
|
||||
/* take care for special value used for indefinite length */
|
||||
if (tlv->value_len == 0xFFFF) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
length_bytes--;
|
||||
}
|
||||
} else { /* data == 0x80 indefinite length form */
|
||||
/* (not allowed for SNMP; RFC 1157, 3.2.2) */
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes positive integer (counter, gauge, timeticks) into u32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded integer field
|
||||
* @param value return host order integer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
|
||||
{
|
||||
u8_t data;
|
||||
|
||||
if ((len > 0) && (len <= 5)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
/* expecting sign bit to be zero, only unsigned please! */
|
||||
if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
|
||||
*value = data;
|
||||
len--;
|
||||
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
*value <<= 8;
|
||||
*value |= data;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes large positive integer (counter64) into 2x u32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded integer field
|
||||
* @param value return host order integer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
|
||||
{
|
||||
u8_t data;
|
||||
|
||||
if (len <= 4) {
|
||||
/* high u32 is 0 */
|
||||
*value = 0;
|
||||
/* directly skip to low u32 */
|
||||
value++;
|
||||
}
|
||||
|
||||
if ((len > 0) && (len <= 9)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
/* expecting sign bit to be zero, only unsigned please! */
|
||||
if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
|
||||
*value = data;
|
||||
len--;
|
||||
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
if (len == 4) {
|
||||
/* skip to low u32 */
|
||||
value++;
|
||||
*value = 0;
|
||||
} else {
|
||||
*value <<= 8;
|
||||
}
|
||||
|
||||
*value |= data;
|
||||
len--;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes integer into s32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded integer field
|
||||
* @param value return host order integer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed!
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
|
||||
{
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
u8_t *lsb_ptr = (u8_t*)value;
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
|
||||
#endif
|
||||
u8_t sign;
|
||||
u8_t data;
|
||||
|
||||
if ((len > 0) && (len < 5)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
if (data & 0x80) {
|
||||
/* negative, start from -1 */
|
||||
*value = -1;
|
||||
sign = 1;
|
||||
*lsb_ptr &= data;
|
||||
} else {
|
||||
/* positive, start from 0 */
|
||||
*value = 0;
|
||||
sign = 0;
|
||||
*lsb_ptr |= data;
|
||||
}
|
||||
|
||||
/* OR/AND octets with value */
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
*value <<= 8;
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
*value >>= 8;
|
||||
#endif
|
||||
|
||||
if (sign) {
|
||||
*lsb_ptr |= 255;
|
||||
*lsb_ptr &= data;
|
||||
} else {
|
||||
*lsb_ptr |= data;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes object identifier from incoming message into array of u32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded object identifier
|
||||
* @param oid return decoded object identifier
|
||||
* @param oid_len return decoded object identifier length
|
||||
* @param oid_max_len size of oid buffer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
|
||||
{
|
||||
u32_t *oid_ptr;
|
||||
u8_t data;
|
||||
|
||||
*oid_len = 0;
|
||||
oid_ptr = oid;
|
||||
if (len > 0) {
|
||||
if (oid_max_len < 2) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
/* first compressed octet */
|
||||
if (data == 0x2B) {
|
||||
/* (most) common case 1.3 (iso.org) */
|
||||
*oid_ptr = 1;
|
||||
oid_ptr++;
|
||||
*oid_ptr = 3;
|
||||
oid_ptr++;
|
||||
} else if (data < 40) {
|
||||
*oid_ptr = 0;
|
||||
oid_ptr++;
|
||||
*oid_ptr = data;
|
||||
oid_ptr++;
|
||||
} else if (data < 80) {
|
||||
*oid_ptr = 1;
|
||||
oid_ptr++;
|
||||
*oid_ptr = data - 40;
|
||||
oid_ptr++;
|
||||
} else {
|
||||
*oid_ptr = 2;
|
||||
oid_ptr++;
|
||||
*oid_ptr = data - 80;
|
||||
oid_ptr++;
|
||||
}
|
||||
*oid_len = 2;
|
||||
} else {
|
||||
/* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
while ((len > 0) && (*oid_len < oid_max_len)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
if ((data & 0x80) == 0x00) {
|
||||
/* sub-identifier uses single octet */
|
||||
*oid_ptr = data;
|
||||
} else {
|
||||
/* sub-identifier uses multiple octets */
|
||||
u32_t sub_id = (data & ~0x80);
|
||||
while ((len > 0) && ((data & 0x80) != 0)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
sub_id = (sub_id << 7) + (data & ~0x80);
|
||||
}
|
||||
|
||||
if ((data & 0x80) != 0) {
|
||||
/* "more bytes following" bit still set at end of len */
|
||||
return ERR_VAL;
|
||||
}
|
||||
*oid_ptr = sub_id;
|
||||
}
|
||||
oid_ptr++;
|
||||
(*oid_len)++;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
/* OID to long to fit in our buffer */
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
|
||||
* from incoming message into array.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded raw data (zero is valid, e.g. empty string!)
|
||||
* @param buf return raw bytes
|
||||
* @param buf_len returns length of the raw return value
|
||||
* @param buf_max_len buffer size
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
|
||||
{
|
||||
if (len > buf_max_len) {
|
||||
/* not enough dst space */
|
||||
return ERR_MEM;
|
||||
}
|
||||
*buf_len = len;
|
||||
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
|
||||
buf++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
1312
ext/lwip/src/apps/snmp/snmp_core.c
Normal file
1312
ext/lwip/src/apps/snmp/snmp_core.c
Normal file
File diff suppressed because it is too large
Load Diff
376
ext/lwip/src/apps/snmp/snmp_mib2_interfaces.c
Normal file
376
ext/lwip/src/apps/snmp/snmp_mib2_interfaces.c
Normal file
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) INTERFACES objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
|
||||
/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
interfaces_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
if (instance->node->oid == 1) {
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
s32_t num_netifs = 0;
|
||||
|
||||
struct netif *netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
num_netifs++;
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
*sint_ptr = num_netifs;
|
||||
return sizeof(*sint_ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
|
||||
{ 1, 0xff } /* netif->num is u8_t */
|
||||
};
|
||||
|
||||
static const u8_t iftable_ifOutQLen = 0;
|
||||
|
||||
static const u8_t iftable_ifOperStatus_up = 1;
|
||||
static const u8_t iftable_ifOperStatus_down = 2;
|
||||
|
||||
static const u8_t iftable_ifAdminStatus_up = 1;
|
||||
static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
|
||||
static const u8_t iftable_ifAdminStatus_down = 2;
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance)
|
||||
{
|
||||
u32_t ifIndex;
|
||||
struct netif *netif;
|
||||
|
||||
LWIP_UNUSED_ARG(column);
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get netif index from incoming OID */
|
||||
ifIndex = row_oid[0];
|
||||
|
||||
/* find netif with index */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
if (netif_to_num(netif) == ifIndex) {
|
||||
/* store netif pointer for subsequent operations (get/test/set) */
|
||||
cell_instance->reference.ptr = netif;
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance)
|
||||
{
|
||||
struct netif *netif;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
|
||||
|
||||
LWIP_UNUSED_ARG(column);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
|
||||
test_oid[0] = netif_to_num(netif);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* store netif pointer for subsequent operations (get/test/set) */
|
||||
cell_instance->reference.ptr = /* (struct netif*) */state.reference;
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static s16_t
|
||||
interfaces_Table_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
struct netif *netif = (struct netif*)instance->reference.ptr;
|
||||
u32_t* value_u32 = (u32_t*)value;
|
||||
s32_t* value_s32 = (s32_t*)value;
|
||||
u16_t value_len;
|
||||
|
||||
switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id))
|
||||
{
|
||||
case 1: /* ifIndex */
|
||||
*value_s32 = netif_to_num(netif);
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 2: /* ifDescr */
|
||||
value_len = sizeof(netif->name);
|
||||
MEMCPY(value, netif->name, value_len);
|
||||
break;
|
||||
case 3: /* ifType */
|
||||
*value_s32 = netif->link_type;
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 4: /* ifMtu */
|
||||
*value_s32 = netif->mtu;
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 5: /* ifSpeed */
|
||||
*value_u32 = netif->link_speed;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 6: /* ifPhysAddress */
|
||||
value_len = sizeof(netif->hwaddr);
|
||||
MEMCPY(value, &netif->hwaddr, value_len);
|
||||
break;
|
||||
case 7: /* ifAdminStatus */
|
||||
if (netif_is_up(netif)) {
|
||||
*value_s32 = iftable_ifOperStatus_up;
|
||||
} else {
|
||||
*value_s32 = iftable_ifOperStatus_down;
|
||||
}
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 8: /* ifOperStatus */
|
||||
if (netif_is_up(netif)) {
|
||||
if (netif_is_link_up(netif)) {
|
||||
*value_s32 = iftable_ifAdminStatus_up;
|
||||
} else {
|
||||
*value_s32 = iftable_ifAdminStatus_lowerLayerDown;
|
||||
}
|
||||
} else {
|
||||
*value_s32 = iftable_ifAdminStatus_down;
|
||||
}
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 9: /* ifLastChange */
|
||||
*value_u32 = netif->ts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 10: /* ifInOctets */
|
||||
*value_u32 = netif->mib2_counters.ifinoctets;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 11: /* ifInUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifinucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 12: /* ifInNUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifinnucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 13: /* ifInDiscards */
|
||||
*value_u32 = netif->mib2_counters.ifindiscards;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 14: /* ifInErrors */
|
||||
*value_u32 = netif->mib2_counters.ifinerrors;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 15: /* ifInUnkownProtos */
|
||||
*value_u32 = netif->mib2_counters.ifinunknownprotos;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 16: /* ifOutOctets */
|
||||
*value_u32 = netif->mib2_counters.ifoutoctets;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 17: /* ifOutUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifoutucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 18: /* ifOutNUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifoutnucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 19: /* ifOutDiscarts */
|
||||
*value_u32 = netif->mib2_counters.ifoutdiscards;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 20: /* ifOutErrors */
|
||||
*value_u32 = netif->mib2_counters.ifouterrors;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 21: /* ifOutQLen */
|
||||
*value_u32 = iftable_ifOutQLen;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
/** @note returning zeroDotZero (0.0) no media specific MIB support */
|
||||
case 22: /* ifSpecific */
|
||||
value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
|
||||
MEMCPY(value, snmp_zero_dot_zero.id, value_len);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value_len;
|
||||
}
|
||||
|
||||
#if !SNMP_SAFE_REQUESTS
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
/* stack should never call this method for another column,
|
||||
because all other columns are set to readonly */
|
||||
LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (*sint_ptr == 1 || *sint_ptr == 2)
|
||||
{
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
return SNMP_ERR_WRONGVALUE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
struct netif *netif = (struct netif*)instance->reference.ptr;
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
/* stack should never call this method for another column,
|
||||
because all other columns are set to readonly */
|
||||
LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (*sint_ptr == 1) {
|
||||
netif_set_up(netif);
|
||||
} else if (*sint_ptr == 2) {
|
||||
netif_set_down(netif);
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
#endif /* SNMP_SAFE_REQUESTS */
|
||||
|
||||
static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
|
||||
|
||||
static const struct snmp_table_col_def interfaces_Table_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
|
||||
{ 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
|
||||
{ 3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
|
||||
{ 5, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
|
||||
{ 6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
|
||||
#if !SNMP_SAFE_REQUESTS
|
||||
{ 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
|
||||
#else
|
||||
{ 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
|
||||
#endif
|
||||
{ 8, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
|
||||
{ 9, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
|
||||
{ 10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
|
||||
{ 11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
|
||||
{ 12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
|
||||
{ 13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
|
||||
{ 14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
|
||||
{ 15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
|
||||
{ 16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
|
||||
{ 17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
|
||||
{ 18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
|
||||
{ 19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
|
||||
{ 20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
|
||||
{ 21, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
|
||||
{ 22, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY } /* ifSpecific */
|
||||
};
|
||||
|
||||
#if !SNMP_SAFE_REQUESTS
|
||||
static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
|
||||
2, interfaces_Table_columns,
|
||||
interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
|
||||
interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
|
||||
#else
|
||||
static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
|
||||
2, interfaces_Table_columns,
|
||||
interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
|
||||
interfaces_Table_get_value, NULL, NULL);
|
||||
#endif
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
|
||||
CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
|
||||
|
||||
static const struct snmp_node* const interface_nodes[] = {
|
||||
&SYNC_NODE_NAME(interfaces_Number).node.node,
|
||||
&SYNC_NODE_NAME(interfaces_Table).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
743
ext/lwip/src/apps/snmp/snmp_mib2_ip.c
Normal file
743
ext/lwip/src/apps/snmp/snmp_mib2_ip.c
Normal file
@@ -0,0 +1,743 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) IP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/etharp.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
ip_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
s32_t* sint_ptr = (s32_t*)value;
|
||||
u32_t* uint_ptr = (u32_t*)value;
|
||||
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* ipForwarding */
|
||||
#if IP_FORWARD
|
||||
/* forwarding */
|
||||
*sint_ptr = 1;
|
||||
#else
|
||||
/* not-forwarding */
|
||||
*sint_ptr = 2;
|
||||
#endif
|
||||
return sizeof(*sint_ptr);
|
||||
case 2: /* ipDefaultTTL */
|
||||
*sint_ptr = IP_DEFAULT_TTL;
|
||||
return sizeof(*sint_ptr);
|
||||
case 3: /* ipInReceives */
|
||||
*uint_ptr = STATS_GET(mib2.ipinreceives);
|
||||
return sizeof(*uint_ptr);
|
||||
case 4: /* ipInHdrErrors */
|
||||
*uint_ptr = STATS_GET(mib2.ipinhdrerrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 5: /* ipInAddrErrors */
|
||||
*uint_ptr = STATS_GET(mib2.ipinaddrerrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 6: /* ipForwDatagrams */
|
||||
*uint_ptr = STATS_GET(mib2.ipforwdatagrams);
|
||||
return sizeof(*uint_ptr);
|
||||
case 7: /* ipInUnknownProtos */
|
||||
*uint_ptr = STATS_GET(mib2.ipinunknownprotos);
|
||||
return sizeof(*uint_ptr);
|
||||
case 8: /* ipInDiscards */
|
||||
*uint_ptr = STATS_GET(mib2.ipindiscards);
|
||||
return sizeof(*uint_ptr);
|
||||
case 9: /* ipInDelivers */
|
||||
*uint_ptr = STATS_GET(mib2.ipindelivers);
|
||||
return sizeof(*uint_ptr);
|
||||
case 10: /* ipOutRequests */
|
||||
*uint_ptr = STATS_GET(mib2.ipoutrequests);
|
||||
return sizeof(*uint_ptr);
|
||||
case 11: /* ipOutDiscards */
|
||||
*uint_ptr = STATS_GET(mib2.ipoutdiscards);
|
||||
return sizeof(*uint_ptr);
|
||||
case 12: /* ipOutNoRoutes */
|
||||
*uint_ptr = STATS_GET(mib2.ipoutnoroutes);
|
||||
return sizeof(*uint_ptr);
|
||||
case 13: /* ipReasmTimeout */
|
||||
#if IP_REASSEMBLY
|
||||
*sint_ptr = IP_REASS_MAXAGE;
|
||||
#else
|
||||
*sint_ptr = 0;
|
||||
#endif
|
||||
return sizeof(*sint_ptr);
|
||||
case 14: /* ipReasmReqds */
|
||||
*uint_ptr = STATS_GET(mib2.ipreasmreqds);
|
||||
return sizeof(*uint_ptr);
|
||||
case 15: /* ipReasmOKs */
|
||||
*uint_ptr = STATS_GET(mib2.ipreasmoks);
|
||||
return sizeof(*uint_ptr);
|
||||
case 16: /* ipReasmFails */
|
||||
*uint_ptr = STATS_GET(mib2.ipreasmfails);
|
||||
return sizeof(*uint_ptr);
|
||||
case 17: /* ipFragOKs */
|
||||
*uint_ptr = STATS_GET(mib2.ipfragoks);
|
||||
return sizeof(*uint_ptr);
|
||||
case 18: /* ipFragFails */
|
||||
*uint_ptr = STATS_GET(mib2.ipfragfails);
|
||||
return sizeof(*uint_ptr);
|
||||
case 19: /* ipFragCreates */
|
||||
*uint_ptr = STATS_GET(mib2.ipfragcreates);
|
||||
return sizeof(*uint_ptr);
|
||||
case 23: /* ipRoutingDiscards: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ip object value before setting.
|
||||
*
|
||||
* @param od is the object definition
|
||||
* @param len return value space (in bytes)
|
||||
* @param value points to (varbind) space to copy value from.
|
||||
*
|
||||
* @note we allow set if the value matches the hardwired value,
|
||||
* otherwise return badvalue.
|
||||
*/
|
||||
static snmp_err_t
|
||||
ip_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_WRONGVALUE;
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
LWIP_UNUSED_ARG(len);
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* ipForwarding */
|
||||
#if IP_FORWARD
|
||||
/* forwarding */
|
||||
if (*sint_ptr == 1)
|
||||
#else
|
||||
/* not-forwarding */
|
||||
if (*sint_ptr == 2)
|
||||
#endif
|
||||
{
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
break;
|
||||
case 2: /* ipDefaultTTL */
|
||||
if (*sint_ptr == IP_DEFAULT_TTL) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
LWIP_UNUSED_ARG(instance);
|
||||
LWIP_UNUSED_ARG(len);
|
||||
LWIP_UNUSED_ARG(value);
|
||||
/* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
/* --- ipAddrTable --- */
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff } /* IP D */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
switch (*column) {
|
||||
case 1: /* ipAdEntAddr */
|
||||
value->u32 = netif_ip4_addr(netif)->addr;
|
||||
break;
|
||||
case 2: /* ipAdEntIfIndex */
|
||||
value->u32 = netif_to_num(netif);
|
||||
break;
|
||||
case 3: /* ipAdEntNetMask */
|
||||
value->u32 = netif_ip4_netmask(netif)->addr;
|
||||
break;
|
||||
case 4: /* ipAdEntBcastAddr */
|
||||
/* lwIP oddity, there's no broadcast
|
||||
address in the netif we can rely on */
|
||||
value->u32 = IPADDR_BROADCAST & 1;
|
||||
break;
|
||||
case 5: /* ipAdEntReasmMaxSize */
|
||||
#if IP_REASSEMBLY
|
||||
/* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
|
||||
* but only if receiving one fragmented packet at a time.
|
||||
* The current solution is to calculate for 2 simultaneous packets...
|
||||
*/
|
||||
value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
|
||||
(PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN)));
|
||||
#else
|
||||
/** @todo returning MTU would be a bad thing and
|
||||
returning a wild guess like '576' isn't good either */
|
||||
value->u32 = 0;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_AddrTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t ip;
|
||||
struct netif *netif;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
|
||||
|
||||
/* find netif with requested ip */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
|
||||
/* fill in object properties */
|
||||
return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
|
||||
}
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_AddrTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct netif *netif;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
|
||||
snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return ip_AddrTable_get_cell_value_core((struct netif*)state.reference, column, value, value_len);
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* --- ipRouteTable --- */
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
switch (*column) {
|
||||
case 1: /* ipRouteDest */
|
||||
if (default_route) {
|
||||
/* default rte has 0.0.0.0 dest */
|
||||
value->u32 = IP4_ADDR_ANY->addr;
|
||||
} else {
|
||||
/* netifs have netaddress dest */
|
||||
ip4_addr_t tmp;
|
||||
ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif));
|
||||
value->u32 = tmp.addr;
|
||||
}
|
||||
break;
|
||||
case 2: /* ipRouteIfIndex */
|
||||
value->u32 = netif_to_num(netif);
|
||||
break;
|
||||
case 3: /* ipRouteMetric1 */
|
||||
if (default_route) {
|
||||
value->s32 = 1; /* default */
|
||||
} else {
|
||||
value->s32 = 0; /* normal */
|
||||
}
|
||||
break;
|
||||
case 4: /* ipRouteMetric2 */
|
||||
case 5: /* ipRouteMetric3 */
|
||||
case 6: /* ipRouteMetric4 */
|
||||
value->s32 = -1; /* none */
|
||||
break;
|
||||
case 7: /* ipRouteNextHop */
|
||||
if (default_route) {
|
||||
/* default rte: gateway */
|
||||
value->u32 = netif_ip4_gw(netif)->addr;
|
||||
} else {
|
||||
/* other rtes: netif ip_addr */
|
||||
value->u32 = netif_ip4_addr(netif)->addr;
|
||||
}
|
||||
break;
|
||||
case 8: /* ipRouteType */
|
||||
if (default_route) {
|
||||
/* default rte is indirect */
|
||||
value->u32 = 4; /* indirect */
|
||||
} else {
|
||||
/* other rtes are direct */
|
||||
value->u32 = 3; /* direct */
|
||||
}
|
||||
break;
|
||||
case 9: /* ipRouteProto */
|
||||
/* locally defined routes */
|
||||
value->u32 = 2; /* local */
|
||||
break;
|
||||
case 10: /* ipRouteAge */
|
||||
/* @todo (sysuptime - timestamp last change) / 100 */
|
||||
value->u32 = 0;
|
||||
break;
|
||||
case 11: /* ipRouteMask */
|
||||
if (default_route) {
|
||||
/* default rte use 0.0.0.0 mask */
|
||||
value->u32 = IP4_ADDR_ANY->addr;
|
||||
} else {
|
||||
/* other rtes use netmask */
|
||||
value->u32 = netif_ip4_netmask(netif)->addr;
|
||||
}
|
||||
break;
|
||||
case 12: /* ipRouteMetric5 */
|
||||
value->s32 = -1; /* none */
|
||||
break;
|
||||
case 13: /* ipRouteInfo */
|
||||
value->const_ptr = snmp_zero_dot_zero.id;
|
||||
*value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t test_ip;
|
||||
struct netif *netif;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP and port from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */
|
||||
|
||||
/* default route is on default netif */
|
||||
if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) {
|
||||
/* fill in object properties */
|
||||
return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len);
|
||||
}
|
||||
|
||||
/* find netif with requested route */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
ip4_addr_t dst;
|
||||
ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
|
||||
|
||||
if (ip4_addr_cmp(&dst, &test_ip)) {
|
||||
/* fill in object properties */
|
||||
return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
|
||||
}
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct netif *netif;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges));
|
||||
|
||||
/* check default route */
|
||||
if (netif_default != NULL) {
|
||||
snmp_ip4_to_oid(IP4_ADDR_ANY, &test_oid[0]);
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default);
|
||||
}
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
ip4_addr_t dst;
|
||||
ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
if (!ip4_addr_isany_val(dst)) {
|
||||
snmp_ip4_to_oid(&dst, &test_oid[0]);
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
|
||||
}
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
ip4_addr_t dst;
|
||||
snmp_oid_to_ip4(&result_temp[0], &dst);
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return ip_RouteTable_get_cell_value_core((struct netif*)state.reference, ip4_addr_isany_val(dst), column, value, value_len);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_ARP && LWIP_IPV4
|
||||
/* --- ipNetToMediaTable --- */
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = {
|
||||
{ 1, 0xff }, /* IfIndex */
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff } /* IP D */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
ip_NetToMediaTable_get_cell_value_core(u8_t arp_table_index, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t *ip;
|
||||
struct netif *netif;
|
||||
struct eth_addr *ethaddr;
|
||||
|
||||
etharp_get_entry(arp_table_index, &ip, &netif, ðaddr);
|
||||
|
||||
/* value */
|
||||
switch (*column) {
|
||||
case 1: /* atIfIndex / ipNetToMediaIfIndex */
|
||||
value->u32 = netif_to_num(netif);
|
||||
break;
|
||||
case 2: /* atPhysAddress / ipNetToMediaPhysAddress */
|
||||
value->ptr = ethaddr;
|
||||
*value_len = sizeof(*ethaddr);
|
||||
break;
|
||||
case 3: /* atNetAddress / ipNetToMediaNetAddress */
|
||||
value->u32 = ip->addr;
|
||||
break;
|
||||
case 4: /* ipNetToMediaType */
|
||||
value->u32 = 3; /* dynamic*/
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_NetToMediaTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t ip_in;
|
||||
u8_t netif_index;
|
||||
u8_t i;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP from incoming OID */
|
||||
netif_index = (u8_t)row_oid[0];
|
||||
snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */
|
||||
|
||||
/* find requested entry */
|
||||
for (i=0; i<ARP_TABLE_SIZE; i++) {
|
||||
ip4_addr_t *ip;
|
||||
struct netif *netif;
|
||||
struct eth_addr *ethaddr;
|
||||
|
||||
if (etharp_get_entry(i, &ip, &netif, ðaddr)) {
|
||||
if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) {
|
||||
/* fill in object properties */
|
||||
return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
u8_t i;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
for (i=0; i<ARP_TABLE_SIZE; i++) {
|
||||
ip4_addr_t *ip;
|
||||
struct netif *netif;
|
||||
struct eth_addr *ethaddr;
|
||||
|
||||
if (etharp_get_entry(i, &ip, &netif, ðaddr)) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
|
||||
|
||||
test_oid[0] = netif_to_num(netif);
|
||||
snmp_ip4_to_oid(ip, &test_oid[1]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), (void*)(size_t)i);
|
||||
}
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return ip_NetToMediaTable_get_cell_value_core((u8_t)(size_t)state.reference, column, value, value_len);
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
#endif /* LWIP_ARP && LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_scalar_node ip_Forwarding = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
|
||||
static const struct snmp_scalar_node ip_DefaultTTL = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
|
||||
static const struct snmp_scalar_node ip_InReceives = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InHdrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InAddrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ForwDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InDelivers = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_OutRequests = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_OutDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_OutNoRoutes = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmTimeout = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmReqds = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmOKs = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmFails = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_FragOKs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_FragFails = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_FragCreates = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
|
||||
static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */
|
||||
{ 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */
|
||||
{ 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */
|
||||
{ 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipAdEntReasmMaxSize */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value);
|
||||
|
||||
static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */
|
||||
{ 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */
|
||||
{ 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */
|
||||
{ 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */
|
||||
{ 6, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */
|
||||
{ 7, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */
|
||||
{ 8, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */
|
||||
{ 9, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */
|
||||
{ 10, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */
|
||||
{ 11, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */
|
||||
{ 12, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */
|
||||
{ 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR } /* ipRouteInfo */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
#if LWIP_ARP && LWIP_IPV4
|
||||
static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */
|
||||
{ 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */
|
||||
{ 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipNetToMediaType */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_ARP && LWIP_IPV4 */
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding)
|
||||
CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL)
|
||||
CREATE_LWIP_SYNC_NODE( 3, ip_InReceives)
|
||||
CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors)
|
||||
CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors)
|
||||
CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams)
|
||||
CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos)
|
||||
CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards)
|
||||
CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers)
|
||||
CREATE_LWIP_SYNC_NODE(10, ip_OutRequests)
|
||||
CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards)
|
||||
CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes)
|
||||
CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout)
|
||||
CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds)
|
||||
CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs)
|
||||
CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails)
|
||||
CREATE_LWIP_SYNC_NODE(17, ip_FragOKs)
|
||||
CREATE_LWIP_SYNC_NODE(18, ip_FragFails)
|
||||
CREATE_LWIP_SYNC_NODE(19, ip_FragCreates)
|
||||
CREATE_LWIP_SYNC_NODE(20, ip_AddrTable)
|
||||
CREATE_LWIP_SYNC_NODE(21, ip_RouteTable)
|
||||
#if LWIP_ARP
|
||||
CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable)
|
||||
#endif /* LWIP_ARP */
|
||||
CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards)
|
||||
|
||||
static const struct snmp_node* const ip_nodes[] = {
|
||||
&SYNC_NODE_NAME(ip_Forwarding).node.node,
|
||||
&SYNC_NODE_NAME(ip_DefaultTTL).node.node,
|
||||
&SYNC_NODE_NAME(ip_InReceives).node.node,
|
||||
&SYNC_NODE_NAME(ip_InHdrErrors).node.node,
|
||||
&SYNC_NODE_NAME(ip_InAddrErrors).node.node,
|
||||
&SYNC_NODE_NAME(ip_ForwDatagrams).node.node,
|
||||
&SYNC_NODE_NAME(ip_InUnknownProtos).node.node,
|
||||
&SYNC_NODE_NAME(ip_InDiscards).node.node,
|
||||
&SYNC_NODE_NAME(ip_InDelivers).node.node,
|
||||
&SYNC_NODE_NAME(ip_OutRequests).node.node,
|
||||
&SYNC_NODE_NAME(ip_OutDiscards).node.node,
|
||||
&SYNC_NODE_NAME(ip_OutNoRoutes).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmTimeout).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmReqds).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmOKs).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmFails).node.node,
|
||||
&SYNC_NODE_NAME(ip_FragOKs).node.node,
|
||||
&SYNC_NODE_NAME(ip_FragFails).node.node,
|
||||
&SYNC_NODE_NAME(ip_FragCreates).node.node,
|
||||
&SYNC_NODE_NAME(ip_AddrTable).node.node,
|
||||
&SYNC_NODE_NAME(ip_RouteTable).node.node,
|
||||
#if LWIP_ARP
|
||||
&SYNC_NODE_NAME(ip_NetToMediaTable).node.node,
|
||||
#endif /* LWIP_ARP */
|
||||
&SYNC_NODE_NAME(ip_RoutingDiscards).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */
|
||||
|
||||
#if LWIP_ARP && LWIP_IPV4
|
||||
/* at node table is a subset of ip_nettomedia table (same rows but less columns) */
|
||||
static const struct snmp_table_simple_col_def at_Table_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */
|
||||
{ 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */
|
||||
{ 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 } /* atNetAddress */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE(1, at_Table)
|
||||
|
||||
static const struct snmp_node* const at_nodes[] = {
|
||||
&SYNC_NODE_NAME(at_Table).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes);
|
||||
#endif /* LWIP_ARP && LWIP_IPV4 */
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
227
ext/lwip/src/apps/snmp/snmp_mib2_snmp.c
Normal file
227
ext/lwip/src/apps/snmp/snmp_mib2_snmp.c
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) SNMP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#define MIB2_AUTH_TRAPS_ENABLED 1
|
||||
#define MIB2_AUTH_TRAPS_DISABLED 2
|
||||
|
||||
/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */
|
||||
static s16_t
|
||||
snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
|
||||
{
|
||||
u32_t *uint_ptr = (u32_t*)value;
|
||||
switch (node->oid) {
|
||||
case 1: /* snmpInPkts */
|
||||
*uint_ptr = snmp_stats.inpkts;
|
||||
break;
|
||||
case 2: /* snmpOutPkts */
|
||||
*uint_ptr = snmp_stats.outpkts;
|
||||
break;
|
||||
case 3: /* snmpInBadVersions */
|
||||
*uint_ptr = snmp_stats.inbadversions;
|
||||
break;
|
||||
case 4: /* snmpInBadCommunityNames */
|
||||
*uint_ptr = snmp_stats.inbadcommunitynames;
|
||||
break;
|
||||
case 5: /* snmpInBadCommunityUses */
|
||||
*uint_ptr = snmp_stats.inbadcommunityuses;
|
||||
break;
|
||||
case 6: /* snmpInASNParseErrs */
|
||||
*uint_ptr = snmp_stats.inasnparseerrs;
|
||||
break;
|
||||
case 8: /* snmpInTooBigs */
|
||||
*uint_ptr = snmp_stats.intoobigs;
|
||||
break;
|
||||
case 9: /* snmpInNoSuchNames */
|
||||
*uint_ptr = snmp_stats.innosuchnames;
|
||||
break;
|
||||
case 10: /* snmpInBadValues */
|
||||
*uint_ptr = snmp_stats.inbadvalues;
|
||||
break;
|
||||
case 11: /* snmpInReadOnlys */
|
||||
*uint_ptr = snmp_stats.inreadonlys;
|
||||
break;
|
||||
case 12: /* snmpInGenErrs */
|
||||
*uint_ptr = snmp_stats.ingenerrs;
|
||||
break;
|
||||
case 13: /* snmpInTotalReqVars */
|
||||
*uint_ptr = snmp_stats.intotalreqvars;
|
||||
break;
|
||||
case 14: /* snmpInTotalSetVars */
|
||||
*uint_ptr = snmp_stats.intotalsetvars;
|
||||
break;
|
||||
case 15: /* snmpInGetRequests */
|
||||
*uint_ptr = snmp_stats.ingetrequests;
|
||||
break;
|
||||
case 16: /* snmpInGetNexts */
|
||||
*uint_ptr = snmp_stats.ingetnexts;
|
||||
break;
|
||||
case 17: /* snmpInSetRequests */
|
||||
*uint_ptr = snmp_stats.insetrequests;
|
||||
break;
|
||||
case 18: /* snmpInGetResponses */
|
||||
*uint_ptr = snmp_stats.ingetresponses;
|
||||
break;
|
||||
case 19: /* snmpInTraps */
|
||||
*uint_ptr = snmp_stats.intraps;
|
||||
break;
|
||||
case 20: /* snmpOutTooBigs */
|
||||
*uint_ptr = snmp_stats.outtoobigs;
|
||||
break;
|
||||
case 21: /* snmpOutNoSuchNames */
|
||||
*uint_ptr = snmp_stats.outnosuchnames;
|
||||
break;
|
||||
case 22: /* snmpOutBadValues */
|
||||
*uint_ptr = snmp_stats.outbadvalues;
|
||||
break;
|
||||
case 24: /* snmpOutGenErrs */
|
||||
*uint_ptr = snmp_stats.outgenerrs;
|
||||
break;
|
||||
case 25: /* snmpOutGetRequests */
|
||||
*uint_ptr = snmp_stats.outgetrequests;
|
||||
break;
|
||||
case 26: /* snmpOutGetNexts */
|
||||
*uint_ptr = snmp_stats.outgetnexts;
|
||||
break;
|
||||
case 27: /* snmpOutSetRequests */
|
||||
*uint_ptr = snmp_stats.outsetrequests;
|
||||
break;
|
||||
case 28: /* snmpOutGetResponses */
|
||||
*uint_ptr = snmp_stats.outgetresponses;
|
||||
break;
|
||||
case 29: /* snmpOutTraps */
|
||||
*uint_ptr = snmp_stats.outtraps;
|
||||
break;
|
||||
case 30: /* snmpEnableAuthenTraps */
|
||||
if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) {
|
||||
*uint_ptr = MIB2_AUTH_TRAPS_DISABLED;
|
||||
} else {
|
||||
*uint_ptr = MIB2_AUTH_TRAPS_ENABLED;
|
||||
}
|
||||
break;
|
||||
case 31: /* snmpSilentDrops */
|
||||
*uint_ptr = 0; /* not supported */
|
||||
break;
|
||||
case 32: /* snmpProxyDrops */
|
||||
*uint_ptr = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %"S32_F"\n", node->oid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sizeof(*uint_ptr);
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_WRONGVALUE;
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (node->oid == 30) {
|
||||
/* snmpEnableAuthenTraps */
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
/* we should have writable non-volatile mem here */
|
||||
if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (node->oid == 30) {
|
||||
/* snmpEnableAuthenTraps */
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) {
|
||||
snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED);
|
||||
} else {
|
||||
snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */
|
||||
static const struct snmp_scalar_array_node_def snmp_nodes[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInPkts */
|
||||
{ 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutPkts */
|
||||
{ 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadVersions */
|
||||
{ 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityNames */
|
||||
{ 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityUses */
|
||||
{ 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInASNParseErrs */
|
||||
{ 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTooBigs */
|
||||
{ 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInNoSuchNames */
|
||||
{10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadValues */
|
||||
{11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInReadOnlys */
|
||||
{12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGenErrs */
|
||||
{13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalReqVars */
|
||||
{14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalSetVars */
|
||||
{15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetRequests */
|
||||
{16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetNexts */
|
||||
{17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInSetRequests */
|
||||
{18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetResponses */
|
||||
{19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTraps */
|
||||
{20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTooBigs */
|
||||
{21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutNoSuchNames */
|
||||
{22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutBadValues */
|
||||
{24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGenErrs */
|
||||
{25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetRequests */
|
||||
{26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetNexts */
|
||||
{27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutSetRequests */
|
||||
{28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetResponses */
|
||||
{29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTraps */
|
||||
{30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */
|
||||
{31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpSilentDrops */
|
||||
{32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} /* snmpProxyDrops */
|
||||
};
|
||||
|
||||
const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
377
ext/lwip/src/apps/snmp/snmp_mib2_system.c
Normal file
377
ext/lwip/src/apps/snmp/snmp_mib2_system.c
Normal file
@@ -0,0 +1,377 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) SYSTEM objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
|
||||
|
||||
/** mib-2.system.sysDescr */
|
||||
static const u8_t sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
|
||||
static const u8_t* sysdescr = sysdescr_default;
|
||||
static const u16_t* sysdescr_len = NULL; /* use strlen for determining len */
|
||||
|
||||
/** mib-2.system.sysContact */
|
||||
static const u8_t syscontact_default[] = SNMP_LWIP_MIB2_SYSCONTACT;
|
||||
static const u8_t* syscontact = syscontact_default;
|
||||
static const u16_t* syscontact_len = NULL; /* use strlen for determining len */
|
||||
static u8_t* syscontact_wr = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
|
||||
static u16_t* syscontact_wr_len = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
|
||||
static u16_t syscontact_bufsize = 0; /* 0=not writable */
|
||||
|
||||
/** mib-2.system.sysName */
|
||||
static const u8_t sysname_default[] = SNMP_LWIP_MIB2_SYSNAME;
|
||||
static const u8_t* sysname = sysname_default;
|
||||
static const u16_t* sysname_len = NULL; /* use strlen for determining len */
|
||||
static u8_t* sysname_wr = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
|
||||
static u16_t* sysname_wr_len = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
|
||||
static u16_t sysname_bufsize = 0; /* 0=not writable */
|
||||
|
||||
/** mib-2.system.sysLocation */
|
||||
static const u8_t syslocation_default[] = SNMP_LWIP_MIB2_SYSLOCATION;
|
||||
static const u8_t* syslocation = syslocation_default;
|
||||
static const u16_t* syslocation_len = NULL; /* use strlen for determining len */
|
||||
static u8_t* syslocation_wr = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
|
||||
static u16_t* syslocation_wr_len = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
|
||||
static u16_t syslocation_bufsize = 0; /* 0=not writable */
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysDescr pointers.
|
||||
*
|
||||
* @param str if non-NULL then copy str pointer
|
||||
* @param len points to string length, excluding zero terminator
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
|
||||
{
|
||||
if (str != NULL) {
|
||||
sysdescr = str;
|
||||
sysdescr_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysContact pointers
|
||||
*
|
||||
* @param ocstr if non-NULL then copy str pointer
|
||||
* @param ocstrlen points to string length, excluding zero terminator.
|
||||
* if set to NULL it is assumed that ocstr is NULL-terminated.
|
||||
* @param bufsize size of the buffer in bytes.
|
||||
* (this is required because the buffer can be overwritten by snmp-set)
|
||||
* if ocstrlen is NULL buffer needs space for terminating 0 byte.
|
||||
* otherwise complete buffer is used for string.
|
||||
* if bufsize is set to 0, the value is regarded as read-only.
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syscontact = ocstr;
|
||||
syscontact_wr = ocstr;
|
||||
syscontact_len = ocstrlen;
|
||||
syscontact_wr_len = ocstrlen;
|
||||
syscontact_bufsize = bufsize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syscontact = ocstr;
|
||||
syscontact_len = ocstrlen;
|
||||
syscontact_wr = NULL;
|
||||
syscontact_wr_len = NULL;
|
||||
syscontact_bufsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysName pointers
|
||||
*
|
||||
* @param ocstr if non-NULL then copy str pointer
|
||||
* @param ocstrlen points to string length, excluding zero terminator.
|
||||
* if set to NULL it is assumed that ocstr is NULL-terminated.
|
||||
* @param bufsize size of the buffer in bytes.
|
||||
* (this is required because the buffer can be overwritten by snmp-set)
|
||||
* if ocstrlen is NULL buffer needs space for terminating 0 byte.
|
||||
* otherwise complete buffer is used for string.
|
||||
* if bufsize is set to 0, the value is regarded as read-only.
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
sysname = ocstr;
|
||||
sysname_wr = ocstr;
|
||||
sysname_len = ocstrlen;
|
||||
sysname_wr_len = ocstrlen;
|
||||
sysname_bufsize = bufsize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* see \ref snmp_mib2_set_sysname but set pointer to readonly memory
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
sysname = ocstr;
|
||||
sysname_len = ocstrlen;
|
||||
sysname_wr = NULL;
|
||||
sysname_wr_len = NULL;
|
||||
sysname_bufsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysLocation pointers
|
||||
*
|
||||
* @param ocstr if non-NULL then copy str pointer
|
||||
* @param ocstrlen points to string length, excluding zero terminator.
|
||||
* if set to NULL it is assumed that ocstr is NULL-terminated.
|
||||
* @param bufsize size of the buffer in bytes.
|
||||
* (this is required because the buffer can be overwritten by snmp-set)
|
||||
* if ocstrlen is NULL buffer needs space for terminating 0 byte.
|
||||
* otherwise complete buffer is used for string.
|
||||
* if bufsize is set to 0, the value is regarded as read-only.
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syslocation = ocstr;
|
||||
syslocation_wr = ocstr;
|
||||
syslocation_len = ocstrlen;
|
||||
syslocation_wr_len = ocstrlen;
|
||||
syslocation_bufsize = bufsize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syslocation = ocstr;
|
||||
syslocation_len = ocstrlen;
|
||||
syslocation_wr = NULL;
|
||||
syslocation_wr_len = NULL;
|
||||
syslocation_bufsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static s16_t
|
||||
system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
|
||||
{
|
||||
const u8_t* var = NULL;
|
||||
const s16_t* var_len;
|
||||
u16_t result;
|
||||
|
||||
switch (node->oid) {
|
||||
case 1: /* sysDescr */
|
||||
var = sysdescr;
|
||||
var_len = (const s16_t*)sysdescr_len;
|
||||
break;
|
||||
case 2: /* sysObjectID */
|
||||
{
|
||||
const struct snmp_obj_id* dev_enterprise_oid = snmp_get_device_enterprise_oid();
|
||||
MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
|
||||
return dev_enterprise_oid->len * sizeof(u32_t);
|
||||
}
|
||||
case 3: /* sysUpTime */
|
||||
MIB2_COPY_SYSUPTIME_TO((u32_t*)value);
|
||||
return sizeof(u32_t);
|
||||
case 4: /* sysContact */
|
||||
var = syscontact;
|
||||
var_len = (const s16_t*)syscontact_len;
|
||||
break;
|
||||
case 5: /* sysName */
|
||||
var = sysname;
|
||||
var_len = (const s16_t*)sysname_len;
|
||||
break;
|
||||
case 6: /* sysLocation */
|
||||
var = syslocation;
|
||||
var_len = (const s16_t*)syslocation_len;
|
||||
break;
|
||||
case 7: /* sysServices */
|
||||
*(s32_t*)value = SNMP_SYSSERVICES;
|
||||
return sizeof(s32_t);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %"S32_F"\n", node->oid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle string values (OID 1,4,5 and 6) */
|
||||
LWIP_ASSERT("", (value != NULL));
|
||||
if (var_len == NULL) {
|
||||
result = (s16_t)strlen((const char*)var);
|
||||
} else {
|
||||
result = *var_len;
|
||||
}
|
||||
MEMCPY(value, var, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_WRONGVALUE;
|
||||
const u16_t* var_bufsize = NULL;
|
||||
const u16_t* var_wr_len;
|
||||
|
||||
LWIP_UNUSED_ARG(value);
|
||||
|
||||
switch (node->oid) {
|
||||
case 4: /* sysContact */
|
||||
var_bufsize = &syscontact_bufsize;
|
||||
var_wr_len = syscontact_wr_len;
|
||||
break;
|
||||
case 5: /* sysName */
|
||||
var_bufsize = &sysname_bufsize;
|
||||
var_wr_len = sysname_wr_len;
|
||||
break;
|
||||
case 6: /* sysLocation */
|
||||
var_bufsize = &syslocation_bufsize;
|
||||
var_wr_len = syslocation_wr_len;
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %"S32_F"\n", node->oid));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* check if value is writable at all */
|
||||
if (*var_bufsize > 0) {
|
||||
if (var_wr_len == NULL) {
|
||||
/* we have to take the terminating 0 into account */
|
||||
if (len < *var_bufsize) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
} else {
|
||||
if (len <= *var_bufsize) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = SNMP_ERR_NOTWRITABLE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
u8_t* var_wr = NULL;
|
||||
u16_t* var_wr_len;
|
||||
|
||||
switch (node->oid) {
|
||||
case 4: /* sysContact */
|
||||
var_wr = syscontact_wr;
|
||||
var_wr_len = syscontact_wr_len;
|
||||
break;
|
||||
case 5: /* sysName */
|
||||
var_wr = sysname_wr;
|
||||
var_wr_len = sysname_wr_len;
|
||||
break;
|
||||
case 6: /* sysLocation */
|
||||
var_wr = syslocation_wr;
|
||||
var_wr_len = syslocation_wr_len;
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %"S32_F"\n", node->oid));
|
||||
return SNMP_ERR_GENERROR;
|
||||
}
|
||||
|
||||
/* no need to check size of target buffer, this was already done in set_test method */
|
||||
LWIP_ASSERT("", var_wr != NULL);
|
||||
MEMCPY(var_wr, value, len);
|
||||
|
||||
if (var_wr_len == NULL) {
|
||||
/* add terminating 0 */
|
||||
var_wr[len] = 0;
|
||||
} else {
|
||||
*var_wr_len = len;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static const struct snmp_scalar_array_node_def system_nodes[] = {
|
||||
{1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysDescr */
|
||||
{2, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysObjectID */
|
||||
{3, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysUpTime */
|
||||
{4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */
|
||||
{5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */
|
||||
{6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */
|
||||
{7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY} /* sysServices */
|
||||
};
|
||||
|
||||
const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
594
ext/lwip/src/apps/snmp/snmp_mib2_tcp.c
Normal file
594
ext/lwip/src/apps/snmp/snmp_mib2_tcp.c
Normal file
@@ -0,0 +1,594 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) TCP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
tcp_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
u32_t *uint_ptr = (u32_t*)value;
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* tcpRtoAlgorithm, vanj(4) */
|
||||
*sint_ptr = 4;
|
||||
return sizeof(*sint_ptr);
|
||||
case 2: /* tcpRtoMin */
|
||||
/* @todo not the actual value, a guess,
|
||||
needs to be calculated */
|
||||
*sint_ptr = 1000;
|
||||
return sizeof(*sint_ptr);
|
||||
case 3: /* tcpRtoMax */
|
||||
/* @todo not the actual value, a guess,
|
||||
needs to be calculated */
|
||||
*sint_ptr = 60000;
|
||||
return sizeof(*sint_ptr);
|
||||
case 4: /* tcpMaxConn */
|
||||
*sint_ptr = MEMP_NUM_TCP_PCB;
|
||||
return sizeof(*sint_ptr);
|
||||
case 5: /* tcpActiveOpens */
|
||||
*uint_ptr = STATS_GET(mib2.tcpactiveopens);
|
||||
return sizeof(*uint_ptr);
|
||||
case 6: /* tcpPassiveOpens */
|
||||
*uint_ptr = STATS_GET(mib2.tcppassiveopens);
|
||||
return sizeof(*uint_ptr);
|
||||
case 7: /* tcpAttemptFails */
|
||||
*uint_ptr = STATS_GET(mib2.tcpattemptfails);
|
||||
return sizeof(*uint_ptr);
|
||||
case 8: /* tcpEstabResets */
|
||||
*uint_ptr = STATS_GET(mib2.tcpestabresets);
|
||||
return sizeof(*uint_ptr);
|
||||
case 9: /* tcpCurrEstab */
|
||||
{
|
||||
u16_t tcpcurrestab = 0;
|
||||
struct tcp_pcb *pcb = tcp_active_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if ((pcb->state == ESTABLISHED) ||
|
||||
(pcb->state == CLOSE_WAIT)) {
|
||||
tcpcurrestab++;
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
*uint_ptr = tcpcurrestab;
|
||||
}
|
||||
return sizeof(*uint_ptr);
|
||||
case 10: /* tcpInSegs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpinsegs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 11: /* tcpOutSegs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpoutsegs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 12: /* tcpRetransSegs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpretranssegs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 14: /* tcpInErrs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpinerrs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 15: /* tcpOutRsts */
|
||||
*uint_ptr = STATS_GET(mib2.tcpoutrsts);
|
||||
return sizeof(*uint_ptr);
|
||||
case 17: /* tcpHCInSegs */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
case 18: /* tcpHCOutSegs */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- tcpConnTable --- */
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
{ 0, 0xffff }, /* Port */
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
{ 0, 0xffff } /* Port */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* value */
|
||||
switch (*column) {
|
||||
case 1: /* tcpConnState */
|
||||
value->u32 = pcb->state + 1;
|
||||
break;
|
||||
case 2: /* tcpConnLocalAddress */
|
||||
value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
|
||||
break;
|
||||
case 3: /* tcpConnLocalPort */
|
||||
value->u32 = pcb->local_port;
|
||||
break;
|
||||
case 4: /* tcpConnRemAddress */
|
||||
if (pcb->state == LISTEN) {
|
||||
value->u32 = IP4_ADDR_ANY->addr;
|
||||
} else {
|
||||
value->u32 = ip_2_ip4(&pcb->remote_ip)->addr;
|
||||
}
|
||||
break;
|
||||
case 5: /* tcpConnRemPort */
|
||||
if (pcb->state == LISTEN) {
|
||||
value->u32 = 0;
|
||||
} else {
|
||||
value->u32 = pcb->remote_port;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LWIP_ASSERT("invalid id", 0);
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
u8_t i;
|
||||
ip4_addr_t local_ip;
|
||||
ip4_addr_t remote_ip;
|
||||
u16_t local_port;
|
||||
u16_t remote_port;
|
||||
struct tcp_pcb *pcb;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IPs and ports from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */
|
||||
local_port = (u16_t)row_oid[4];
|
||||
snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */
|
||||
remote_port = (u16_t)row_oid[9];
|
||||
|
||||
/* find tcp_pcb with requested ips and ports */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
|
||||
pcb = *tcp_pcb_lists[i];
|
||||
|
||||
while (pcb != NULL) {
|
||||
/* do local IP and local port match? */
|
||||
if (IP_IS_V4_VAL(pcb->local_ip) &&
|
||||
ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) {
|
||||
|
||||
/* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
|
||||
if (pcb->state == LISTEN) {
|
||||
if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY) && (remote_port == 0)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
|
||||
}
|
||||
} else {
|
||||
if (IP_IS_V4_VAL(pcb->remote_ip) &&
|
||||
ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
u8_t i;
|
||||
struct tcp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
|
||||
pcb = *tcp_pcb_lists[i];
|
||||
while (pcb != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
|
||||
|
||||
if (IP_IS_V4_VAL(pcb->local_ip)) {
|
||||
snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
|
||||
test_oid[4] = pcb->local_port;
|
||||
|
||||
/* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
|
||||
if (pcb->state == LISTEN) {
|
||||
snmp_ip4_to_oid(IP4_ADDR_ANY, &test_oid[5]);
|
||||
test_oid[9] = 0;
|
||||
} else {
|
||||
if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */
|
||||
continue;
|
||||
}
|
||||
snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]);
|
||||
test_oid[9] = pcb->remote_port;
|
||||
}
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb);
|
||||
}
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return tcp_ConnTable_get_cell_value_core((struct tcp_pcb*)state.reference, column, value, value_len);
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/* --- tcpConnectionTable --- */
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnectionTable_get_cell_value_core(const u32_t* column, struct tcp_pcb *pcb, union snmp_variant_value* value)
|
||||
{
|
||||
/* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
|
||||
switch (*column) {
|
||||
case 7: /* tcpConnectionState */
|
||||
value->u32 = pcb->state + 1;
|
||||
break;
|
||||
case 8: /* tcpConnectionProcess */
|
||||
value->u32 = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnectionTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip_addr_t local_ip, remote_ip;
|
||||
u16_t local_port, remote_port;
|
||||
struct tcp_pcb *pcb;
|
||||
u8_t idx = 0;
|
||||
u8_t i;
|
||||
struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* find tcp_pcb with requested ip and port*/
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
|
||||
pcb = *tcp_pcb_nonlisten_lists[i];
|
||||
|
||||
while (pcb != NULL) {
|
||||
if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
|
||||
(local_port == pcb->local_port) &&
|
||||
ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
|
||||
(remote_port == pcb->remote_port)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ConnectionTable_get_cell_value_core(column, pcb, value);
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct tcp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
/* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress + 1x tcpConnectionLocalPort
|
||||
* 1x tcpConnectionRemAddressType + 1x OID len + 16x tcpConnectionRemAddress + 1x tcpConnectionRemPort */
|
||||
u32_t result_temp[38];
|
||||
u8_t i;
|
||||
struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
|
||||
pcb = *tcp_pcb_nonlisten_lists[i];
|
||||
|
||||
while (pcb != NULL) {
|
||||
u8_t idx = 0;
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
|
||||
|
||||
/* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
|
||||
|
||||
/* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, idx, pcb);
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb*)state.reference, value);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- tcpListenerTable --- */
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ListenerTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
|
||||
{
|
||||
/* all items except tcpListenerProcess are declared as not-accessible */
|
||||
switch (*column) {
|
||||
case 4: /* tcpListenerProcess */
|
||||
value->u32 = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ListenerTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip_addr_t local_ip;
|
||||
u16_t local_port;
|
||||
struct tcp_pcb_listen *pcb;
|
||||
u8_t idx = 0;
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* find tcp_pcb with requested ip and port*/
|
||||
pcb = tcp_listen_pcbs.listen_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
|
||||
(local_port == pcb->local_port)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ListenerTable_get_cell_value_core(column, value);
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct tcp_pcb_listen *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
/* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress + 1x tcpListenerLocalPort */
|
||||
u32_t result_temp[19];
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
pcb = tcp_listen_pcbs.listen_pcbs;
|
||||
while (pcb != NULL) {
|
||||
u8_t idx = 0;
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
|
||||
|
||||
/* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, idx, NULL);
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return tcp_ListenerTable_get_cell_value_core(column, value);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct snmp_scalar_node tcp_RtoAlgorithm = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_RtoMin = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_RtoMax = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_MaxConn = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_ActiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_PassiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_AttemptFails = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_EstabResets = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_CurrEstab = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_InSegs = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_OutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_RetransSegs = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_InErrs = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_OutRsts = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_HCInSegs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_HCOutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
|
||||
|
||||
#if LWIP_IPV4
|
||||
static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */
|
||||
{ 2, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */
|
||||
{ 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */
|
||||
{ 4, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */
|
||||
{ 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnRemPort */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = {
|
||||
/* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
|
||||
{ 7, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */
|
||||
{ 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnectionProcess */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value);
|
||||
|
||||
|
||||
static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = {
|
||||
/* all items except tcpListenerProcess are declared as not-accessible */
|
||||
{ 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpListenerProcess */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value);
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm)
|
||||
CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin)
|
||||
CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax)
|
||||
CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn)
|
||||
CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens)
|
||||
CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens)
|
||||
CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails)
|
||||
CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets)
|
||||
CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab)
|
||||
CREATE_LWIP_SYNC_NODE(10, tcp_InSegs)
|
||||
CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs)
|
||||
CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs)
|
||||
#if LWIP_IPV4
|
||||
CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
|
||||
#endif /* LWIP_IPV4 */
|
||||
CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
|
||||
CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
|
||||
CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
|
||||
CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
|
||||
CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
|
||||
CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
|
||||
|
||||
static const struct snmp_node* const tcp_nodes[] = {
|
||||
&SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node,
|
||||
&SYNC_NODE_NAME(tcp_RtoMin).node.node,
|
||||
&SYNC_NODE_NAME(tcp_RtoMax).node.node,
|
||||
&SYNC_NODE_NAME(tcp_MaxConn).node.node,
|
||||
&SYNC_NODE_NAME(tcp_ActiveOpens).node.node,
|
||||
&SYNC_NODE_NAME(tcp_PassiveOpens).node.node,
|
||||
&SYNC_NODE_NAME(tcp_AttemptFails).node.node,
|
||||
&SYNC_NODE_NAME(tcp_EstabResets).node.node,
|
||||
&SYNC_NODE_NAME(tcp_CurrEstab).node.node,
|
||||
&SYNC_NODE_NAME(tcp_InSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_OutSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_RetransSegs).node.node,
|
||||
#if LWIP_IPV4
|
||||
&SYNC_NODE_NAME(tcp_ConnTable).node.node,
|
||||
#endif /* LWIP_IPV4 */
|
||||
&SYNC_NODE_NAME(tcp_InErrs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_OutRsts).node.node,
|
||||
&SYNC_NODE_NAME(tcp_HCInSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
|
||||
&SYNC_NODE_NAME(tcp_ListenerTable).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes);
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */
|
||||
357
ext/lwip/src/apps/snmp/snmp_mib2_udp.c
Normal file
357
ext/lwip/src/apps/snmp/snmp_mib2_udp.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) UDP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
udp_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
u32_t *uint_ptr = (u32_t*)value;
|
||||
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* udpInDatagrams */
|
||||
*uint_ptr = STATS_GET(mib2.udpindatagrams);
|
||||
return sizeof(*uint_ptr);
|
||||
case 2: /* udpNoPorts */
|
||||
*uint_ptr = STATS_GET(mib2.udpnoports);
|
||||
return sizeof(*uint_ptr);
|
||||
case 3: /* udpInErrors */
|
||||
*uint_ptr = STATS_GET(mib2.udpinerrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 4: /* udpOutDatagrams */
|
||||
*uint_ptr = STATS_GET(mib2.udpoutdatagrams);
|
||||
return sizeof(*uint_ptr);
|
||||
case 8: /* udpHCInDatagrams */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
case 9: /* udpHCOutDatagrams */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- udpEndpointTable --- */
|
||||
|
||||
static snmp_err_t
|
||||
udp_endpointTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
|
||||
{
|
||||
/* all items except udpEndpointProcess are declared as not-accessible */
|
||||
switch (*column) {
|
||||
case 8: /* udpEndpointProcess */
|
||||
value->u32 = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_endpointTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip_addr_t local_ip, remote_ip;
|
||||
u16_t local_port, remote_port;
|
||||
struct udp_pcb *pcb;
|
||||
u8_t idx = 0;
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* udpEndpointInstance */
|
||||
if (row_oid_len < (idx+1)) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
if (row_oid[idx] != 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* find udp_pcb with requested ip and port*/
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
|
||||
(local_port == pcb->local_port) &&
|
||||
ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
|
||||
(remote_port == pcb->remote_port)) {
|
||||
/* fill in object properties */
|
||||
return udp_endpointTable_get_cell_value_core(column, value);
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_endpointTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct udp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
/* 1x udpEndpointLocalAddressType + 1x OID len + 16x udpEndpointLocalAddress + 1x udpEndpointLocalPort +
|
||||
* 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort +
|
||||
* 1x udpEndpointInstance = 39
|
||||
*/
|
||||
u32_t result_temp[39];
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
|
||||
u8_t idx = 0;
|
||||
|
||||
/* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
|
||||
|
||||
/* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
|
||||
|
||||
test_oid[idx] = 0; /* udpEndpointInstance */
|
||||
idx++;
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, idx, NULL);
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return udp_endpointTable_get_cell_value_core(column, value);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- udpTable --- */
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range udp_Table_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
{ 1, 0xffff } /* Port */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
switch (*column) {
|
||||
case 1: /* udpLocalAddress */
|
||||
/* set reference to PCB local IP and return a generic node that copies IP4 addresses */
|
||||
value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
|
||||
break;
|
||||
case 2: /* udpLocalPort */
|
||||
/* set reference to PCB local port and return a generic node that copies u16_t values */
|
||||
value->u32 = pcb->local_port;
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_Table_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t ip;
|
||||
u16_t port;
|
||||
struct udp_pcb *pcb;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP and port from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
|
||||
port = (u16_t)row_oid[4];
|
||||
|
||||
/* find udp_pcb with requested ip and port*/
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if (IP_IS_V4_VAL(pcb->local_ip)) {
|
||||
if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) {
|
||||
/* fill in object properties */
|
||||
return udp_Table_get_cell_value_core(pcb, column, value, value_len);
|
||||
}
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_Table_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct udp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
|
||||
|
||||
if (IP_IS_V4_VAL(pcb->local_ip)) {
|
||||
snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
|
||||
test_oid[4] = pcb->local_port;
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb);
|
||||
}
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return udp_Table_get_cell_value_core((struct udp_pcb*)state.reference, column, value, value_len);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_scalar_node udp_inDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_noPorts = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_inErrors = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_outDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_HCInDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
|
||||
|
||||
#if LWIP_IPV4
|
||||
static const struct snmp_table_simple_col_def udp_Table_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */
|
||||
{ 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpLocalPort */
|
||||
};
|
||||
static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = {
|
||||
/* all items except udpEndpointProcess are declared as not-accessible */
|
||||
{ 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpEndpointProcess */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value);
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams)
|
||||
CREATE_LWIP_SYNC_NODE(2, udp_noPorts)
|
||||
CREATE_LWIP_SYNC_NODE(3, udp_inErrors)
|
||||
CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
|
||||
#if LWIP_IPV4
|
||||
CREATE_LWIP_SYNC_NODE(5, udp_Table)
|
||||
#endif /* LWIP_IPV4 */
|
||||
CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
|
||||
CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
|
||||
CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
|
||||
|
||||
static const struct snmp_node* const udp_nodes[] = {
|
||||
&SYNC_NODE_NAME(udp_inDatagrams).node.node,
|
||||
&SYNC_NODE_NAME(udp_noPorts).node.node,
|
||||
&SYNC_NODE_NAME(udp_inErrors).node.node,
|
||||
&SYNC_NODE_NAME(udp_outDatagrams).node.node,
|
||||
#if LWIP_IPV4
|
||||
&SYNC_NODE_NAME(udp_Table).node.node,
|
||||
#endif /* LWIP_IPV4 */
|
||||
&SYNC_NODE_NAME(udp_endpointTable).node.node,
|
||||
&SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
|
||||
&SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */
|
||||
1657
ext/lwip/src/apps/snmp/snmp_msg.c
Normal file
1657
ext/lwip/src/apps/snmp/snmp_msg.c
Normal file
File diff suppressed because it is too large
Load Diff
193
ext/lwip/src/apps/snmp/snmp_msg.h
Normal file
193
ext/lwip/src/apps/snmp/snmp_msg.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP Agent message handling structures (internal API, do not use in client code).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* Copyright (c) 2016 Elias Oenal.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Christiaan Simons <christiaan.simons@axon.tv>
|
||||
* Martin Hentschel <info@cl-soft.de>
|
||||
* Elias Oenal <lwip@eliasoenal.com>
|
||||
*/
|
||||
|
||||
#ifndef LWIP_HDR_APPS_SNMP_MSG_H
|
||||
#define LWIP_HDR_APPS_SNMP_MSG_H
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP
|
||||
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "snmp_pbuf_stream.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/err.h"
|
||||
|
||||
#if LWIP_SNMP_V3
|
||||
#include "snmpv3_priv.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The listen port of the SNMP agent. Clients have to make their requests to
|
||||
this port. Most standard clients won't work if you change this! */
|
||||
#ifndef SNMP_IN_PORT
|
||||
#define SNMP_IN_PORT 161
|
||||
#endif
|
||||
/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
|
||||
work if you change this! */
|
||||
#ifndef SNMP_TRAP_PORT
|
||||
#define SNMP_TRAP_PORT 162
|
||||
#endif
|
||||
|
||||
/* version defines used in PDU */
|
||||
#define SNMP_VERSION_1 0
|
||||
#define SNMP_VERSION_2c 1
|
||||
#define SNMP_VERSION_3 3
|
||||
|
||||
struct snmp_varbind_enumerator
|
||||
{
|
||||
struct snmp_pbuf_stream pbuf_stream;
|
||||
u16_t varbind_count;
|
||||
};
|
||||
|
||||
typedef u8_t snmp_vb_enumerator_err_t;
|
||||
#define SNMP_VB_ENUMERATOR_ERR_OK 0
|
||||
#define SNMP_VB_ENUMERATOR_ERR_EOVB 1
|
||||
#define SNMP_VB_ENUMERATOR_ERR_ASN1ERROR 2
|
||||
#define SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH 3
|
||||
|
||||
void snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length);
|
||||
snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind);
|
||||
|
||||
struct snmp_request
|
||||
{
|
||||
/* Communication handle */
|
||||
void *handle;
|
||||
/* source IP address */
|
||||
const ip_addr_t *source_ip;
|
||||
/* source UDP port */
|
||||
u16_t source_port;
|
||||
/* incoming snmp version */
|
||||
u8_t version;
|
||||
/* community name (zero terminated) */
|
||||
u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
|
||||
/* community string length (exclusive zero term) */
|
||||
u16_t community_strlen;
|
||||
/* request type */
|
||||
u8_t request_type;
|
||||
/* request ID */
|
||||
s32_t request_id;
|
||||
/* error status */
|
||||
s32_t error_status;
|
||||
/* error index */
|
||||
s32_t error_index;
|
||||
/* non-repeaters (getBulkRequest (SNMPv2c)) */
|
||||
s32_t non_repeaters;
|
||||
/* max-repetitions (getBulkRequest (SNMPv2c)) */
|
||||
s32_t max_repetitions;
|
||||
|
||||
#if LWIP_SNMP_V3
|
||||
s32_t msg_id;
|
||||
s32_t msg_max_size;
|
||||
u8_t msg_flags;
|
||||
s32_t msg_security_model;
|
||||
u8_t msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
|
||||
u8_t msg_authoritative_engine_id_len;
|
||||
s32_t msg_authoritative_engine_boots;
|
||||
s32_t msg_authoritative_engine_time;
|
||||
u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH];
|
||||
u8_t msg_user_name_len;
|
||||
u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
|
||||
u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
|
||||
u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
|
||||
u8_t context_engine_id_len;
|
||||
u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
|
||||
u8_t context_name_len;
|
||||
#endif
|
||||
|
||||
struct pbuf *inbound_pbuf;
|
||||
struct snmp_varbind_enumerator inbound_varbind_enumerator;
|
||||
u16_t inbound_varbind_offset;
|
||||
u16_t inbound_varbind_len;
|
||||
u16_t inbound_padding_len;
|
||||
|
||||
struct pbuf *outbound_pbuf;
|
||||
struct snmp_pbuf_stream outbound_pbuf_stream;
|
||||
u16_t outbound_pdu_offset;
|
||||
u16_t outbound_error_status_offset;
|
||||
u16_t outbound_error_index_offset;
|
||||
u16_t outbound_varbind_offset;
|
||||
#if LWIP_SNMP_V3
|
||||
u16_t outbound_msg_global_data_offset;
|
||||
u16_t outbound_msg_global_data_end;
|
||||
u16_t outbound_msg_security_parameters_str_offset;
|
||||
u16_t outbound_msg_security_parameters_seq_offset;
|
||||
u16_t outbound_msg_security_parameters_end;
|
||||
u16_t outbound_msg_authentication_parameters_offset;
|
||||
u16_t outbound_scoped_pdu_seq_offset;
|
||||
u16_t outbound_scoped_pdu_string_offset;
|
||||
#endif
|
||||
|
||||
u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
|
||||
};
|
||||
|
||||
/** A helper struct keeping length information about varbinds */
|
||||
struct snmp_varbind_len
|
||||
{
|
||||
u8_t vb_len_len;
|
||||
u16_t vb_value_len;
|
||||
u8_t oid_len_len;
|
||||
u16_t oid_value_len;
|
||||
u8_t value_len_len;
|
||||
u16_t value_value_len;
|
||||
};
|
||||
|
||||
/** Agent community string */
|
||||
extern const char *snmp_community;
|
||||
/** Agent community string for write access */
|
||||
extern const char *snmp_community_write;
|
||||
/** handle for sending traps */
|
||||
extern void* snmp_traps_handle;
|
||||
|
||||
void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
|
||||
err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
|
||||
u8_t snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result);
|
||||
err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len);
|
||||
err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
|
||||
#endif /* LWIP_HDR_APPS_SNMP_MSG_H */
|
||||
136
ext/lwip/src/apps/snmp/snmpv3.c
Normal file
136
ext/lwip/src/apps/snmp/snmpv3.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @file
|
||||
* Additional SNMPv3 functionality RFC3414 and RFC3826.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Elias Oenal.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Elias Oenal <lwip@eliasoenal.com>
|
||||
*/
|
||||
|
||||
#include "snmpv3_priv.h"
|
||||
#include "lwip/apps/snmpv3.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && LWIP_SNMP_V3
|
||||
|
||||
#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
|
||||
#include LWIP_SNMPV3_INCLUDE_ENGINE
|
||||
#endif
|
||||
|
||||
#define SNMP_MAX_TIME_BOOT 2147483647UL
|
||||
|
||||
/** Call this if engine has been changed. Has to reset boots, see below */
|
||||
void
|
||||
snmpv3_engine_id_changed(void)
|
||||
{
|
||||
snmpv3_set_engine_boots(0);
|
||||
}
|
||||
|
||||
/** According to RFC3414 2.2.2.
|
||||
*
|
||||
* The number of times that the SNMP engine has
|
||||
* (re-)initialized itself since snmpEngineID
|
||||
* was last configured.
|
||||
*/
|
||||
u32_t
|
||||
snmpv3_get_engine_boots_internal(void)
|
||||
{
|
||||
if (snmpv3_get_engine_boots() == 0 ||
|
||||
snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) {
|
||||
return snmpv3_get_engine_boots();
|
||||
}
|
||||
|
||||
snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
|
||||
return snmpv3_get_engine_boots();
|
||||
}
|
||||
|
||||
/** RFC3414 2.2.2.
|
||||
*
|
||||
* Once the timer reaches 2147483647 it gets reset to zero and the
|
||||
* engine boot ups get incremented.
|
||||
*/
|
||||
u32_t
|
||||
snmpv3_get_engine_time_internal(void)
|
||||
{
|
||||
if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
|
||||
snmpv3_reset_engine_time();
|
||||
|
||||
if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) {
|
||||
snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1);
|
||||
} else {
|
||||
snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
|
||||
}
|
||||
}
|
||||
|
||||
return snmpv3_get_engine_time();
|
||||
}
|
||||
|
||||
#if LWIP_SNMP_V3_CRYPTO
|
||||
|
||||
/* This function ignores the byte order suggestion in RFC3414
|
||||
* since it simply doesn't influence the effectiveness of an IV.
|
||||
*
|
||||
* Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
|
||||
*
|
||||
* @todo: This is a potential thread safety issue.
|
||||
*/
|
||||
err_t
|
||||
snmpv3_build_priv_param(u8_t* priv_param)
|
||||
{
|
||||
#ifdef LWIP_RAND /* Based on RFC3826 */
|
||||
static u8_t init;
|
||||
static u32_t priv1, priv2;
|
||||
|
||||
/* Lazy initialisation */
|
||||
if (init == 0) {
|
||||
init = 1;
|
||||
priv1 = LWIP_RAND();
|
||||
priv2 = LWIP_RAND();
|
||||
}
|
||||
|
||||
memcpy(&priv_param[0], &priv1, sizeof(priv1));
|
||||
memcpy(&priv_param[4], &priv2, sizeof(priv2));
|
||||
|
||||
/* Emulate 64bit increment */
|
||||
priv1++;
|
||||
if (!priv1) { /* Overflow */
|
||||
priv2++;
|
||||
}
|
||||
#else /* Based on RFC3414 */
|
||||
static u32_t ctr;
|
||||
u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS();
|
||||
memcpy(&priv_param[0], &boots, 4);
|
||||
memcpy(&priv_param[4], &ctr, 4);
|
||||
ctr++;
|
||||
#endif
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_SNMP_V3_CRYPTO */
|
||||
|
||||
#endif
|
||||
331
ext/lwip/src/apps/snmp/snmpv3_mbedtls.c
Normal file
331
ext/lwip/src/apps/snmp/snmpv3_mbedtls.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMPv3 crypto/auth functions implemented for ARM mbedtls.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Elias Oenal <lwip@eliasoenal.com>
|
||||
* Dirk Ziegelmeier <dirk@ziegelmeier.net>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmpv3.h"
|
||||
#include "snmpv3_priv.h"
|
||||
#include "lwip/arch.h"
|
||||
#include "snmp_msg.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS
|
||||
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/cipher.h"
|
||||
|
||||
#include "mbedtls/md5.h"
|
||||
#include "mbedtls/sha1.h"
|
||||
|
||||
err_t
|
||||
snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length,
|
||||
const u8_t* key, u8_t algo, u8_t* hmac_out)
|
||||
{
|
||||
u32_t i;
|
||||
u8_t key_len;
|
||||
const mbedtls_md_info_t *md_info;
|
||||
mbedtls_md_context_t ctx;
|
||||
struct snmp_pbuf_stream read_stream;
|
||||
snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
|
||||
|
||||
if (algo == SNMP_V3_AUTH_ALGO_MD5) {
|
||||
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
|
||||
key_len = SNMP_V3_MD5_LEN;
|
||||
} else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
|
||||
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
|
||||
key_len = SNMP_V3_SHA_LEN;
|
||||
} else {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
mbedtls_md_init(&ctx);
|
||||
if(mbedtls_md_setup(&ctx, md_info, 1) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
|
||||
goto free_md;
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
u8_t byte;
|
||||
|
||||
if (snmp_pbuf_stream_read(&read_stream, &byte)) {
|
||||
goto free_md;
|
||||
}
|
||||
|
||||
if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) {
|
||||
goto free_md;
|
||||
}
|
||||
}
|
||||
|
||||
if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) {
|
||||
goto free_md;
|
||||
}
|
||||
|
||||
mbedtls_md_free(&ctx);
|
||||
return ERR_OK;
|
||||
|
||||
free_md:
|
||||
mbedtls_md_free(&ctx);
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
#if LWIP_SNMP_V3_CRYPTO
|
||||
|
||||
err_t
|
||||
snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
|
||||
const u8_t* key, const u8_t* priv_param, const u32_t engine_boots,
|
||||
const u32_t engine_time, u8_t algo, u8_t mode)
|
||||
{
|
||||
size_t i;
|
||||
mbedtls_cipher_context_t ctx;
|
||||
const mbedtls_cipher_info_t *cipher_info;
|
||||
|
||||
struct snmp_pbuf_stream read_stream;
|
||||
struct snmp_pbuf_stream write_stream;
|
||||
snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
|
||||
snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length);
|
||||
mbedtls_cipher_init(&ctx);
|
||||
|
||||
if (algo == SNMP_V3_PRIV_ALGO_DES) {
|
||||
u8_t iv_local[8];
|
||||
u8_t out_bytes[8];
|
||||
size_t out_len;
|
||||
|
||||
/* RFC 3414 mandates padding for DES */
|
||||
if ((length & 0x07) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC);
|
||||
if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if(mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if(mbedtls_cipher_setkey(&ctx, key, 8*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Prepare IV */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) {
|
||||
iv_local[i] = priv_param[i] ^ key[i + 8];
|
||||
}
|
||||
if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i += 8) {
|
||||
size_t j;
|
||||
u8_t in_bytes[8];
|
||||
out_len = LWIP_ARRAYSIZE(out_bytes) ;
|
||||
|
||||
for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
|
||||
snmp_pbuf_stream_read(&read_stream, &in_bytes[j]);
|
||||
}
|
||||
|
||||
if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
|
||||
}
|
||||
|
||||
out_len = LWIP_ARRAYSIZE(out_bytes);
|
||||
if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
|
||||
goto error;
|
||||
}
|
||||
snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
|
||||
} else if (algo == SNMP_V3_PRIV_ALGO_AES) {
|
||||
u8_t iv_local[16];
|
||||
|
||||
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
|
||||
if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* IV is the big endian concatenation of boots,
|
||||
* uptime and priv param - see RFC3826.
|
||||
*/
|
||||
iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
|
||||
iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
|
||||
iv_local[0 + 2] = (engine_boots >> 8) & 0xFF;
|
||||
iv_local[0 + 3] = (engine_boots >> 0) & 0xFF;
|
||||
iv_local[4 + 0] = (engine_time >> 24) & 0xFF;
|
||||
iv_local[4 + 1] = (engine_time >> 16) & 0xFF;
|
||||
iv_local[4 + 2] = (engine_time >> 8) & 0xFF;
|
||||
iv_local[4 + 3] = (engine_time >> 0) & 0xFF;
|
||||
memcpy(iv_local + 8, priv_param, 8);
|
||||
if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
u8_t in_byte;
|
||||
u8_t out_byte;
|
||||
size_t out_len = sizeof(out_byte);
|
||||
|
||||
snmp_pbuf_stream_read(&read_stream, &in_byte);
|
||||
if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
|
||||
goto error;
|
||||
}
|
||||
snmp_pbuf_stream_write(&write_stream, out_byte);
|
||||
}
|
||||
} else {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
mbedtls_cipher_free(&ctx);
|
||||
return ERR_OK;
|
||||
|
||||
error:
|
||||
mbedtls_cipher_free(&ctx);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP_V3_CRYPTO */
|
||||
|
||||
/* A.2.1. Password to Key Sample Code for MD5 */
|
||||
void
|
||||
snmpv3_password_to_key_md5(
|
||||
const u8_t *password, /* IN */
|
||||
u8_t passwordlen, /* IN */
|
||||
const u8_t *engineID, /* IN - pointer to snmpEngineID */
|
||||
u8_t engineLength,/* IN - length of snmpEngineID */
|
||||
u8_t *key) /* OUT - pointer to caller 16-octet buffer */
|
||||
{
|
||||
mbedtls_md5_context MD;
|
||||
u8_t *cp, password_buf[64];
|
||||
u32_t password_index = 0;
|
||||
u8_t i;
|
||||
u32_t count = 0;
|
||||
|
||||
mbedtls_md5_init(&MD); /* initialize MD5 */
|
||||
mbedtls_md5_starts(&MD);
|
||||
|
||||
/**********************************************/
|
||||
/* Use while loop until we've done 1 Megabyte */
|
||||
/**********************************************/
|
||||
while (count < 1048576) {
|
||||
cp = password_buf;
|
||||
for (i = 0; i < 64; i++) {
|
||||
/*************************************************/
|
||||
/* Take the next octet of the password, wrapping */
|
||||
/* to the beginning of the password as necessary.*/
|
||||
/*************************************************/
|
||||
*cp++ = password[password_index++ % passwordlen];
|
||||
}
|
||||
mbedtls_md5_update(&MD, password_buf, 64);
|
||||
count += 64;
|
||||
}
|
||||
mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */
|
||||
|
||||
/*****************************************************/
|
||||
/* Now localize the key with the engineID and pass */
|
||||
/* through MD5 to produce final key */
|
||||
/* May want to ensure that engineLength <= 32, */
|
||||
/* otherwise need to use a buffer larger than 64 */
|
||||
/*****************************************************/
|
||||
memcpy(password_buf, key, 16);
|
||||
memcpy(password_buf + 16, engineID, engineLength);
|
||||
memcpy(password_buf + 16 + engineLength, key, 16);
|
||||
|
||||
mbedtls_md5_starts(&MD);
|
||||
mbedtls_md5_update(&MD, password_buf, 32 + engineLength);
|
||||
mbedtls_md5_finish(&MD, key);
|
||||
|
||||
mbedtls_md5_free(&MD);
|
||||
return;
|
||||
}
|
||||
|
||||
/* A.2.2. Password to Key Sample Code for SHA */
|
||||
void
|
||||
snmpv3_password_to_key_sha(
|
||||
const u8_t *password, /* IN */
|
||||
u8_t passwordlen, /* IN */
|
||||
const u8_t *engineID, /* IN - pointer to snmpEngineID */
|
||||
u8_t engineLength,/* IN - length of snmpEngineID */
|
||||
u8_t *key) /* OUT - pointer to caller 20-octet buffer */
|
||||
{
|
||||
mbedtls_sha1_context SH;
|
||||
u8_t *cp, password_buf[72];
|
||||
u32_t password_index = 0;
|
||||
u8_t i;
|
||||
u32_t count = 0;
|
||||
|
||||
mbedtls_sha1_init(&SH); /* initialize SHA */
|
||||
mbedtls_sha1_starts(&SH);
|
||||
|
||||
/**********************************************/
|
||||
/* Use while loop until we've done 1 Megabyte */
|
||||
/**********************************************/
|
||||
while (count < 1048576) {
|
||||
cp = password_buf;
|
||||
for (i = 0; i < 64; i++) {
|
||||
/*************************************************/
|
||||
/* Take the next octet of the password, wrapping */
|
||||
/* to the beginning of the password as necessary.*/
|
||||
/*************************************************/
|
||||
*cp++ = password[password_index++ % passwordlen];
|
||||
}
|
||||
mbedtls_sha1_update(&SH, password_buf, 64);
|
||||
count += 64;
|
||||
}
|
||||
mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */
|
||||
|
||||
/*****************************************************/
|
||||
/* Now localize the key with the engineID and pass */
|
||||
/* through SHA to produce final key */
|
||||
/* May want to ensure that engineLength <= 32, */
|
||||
/* otherwise need to use a buffer larger than 72 */
|
||||
/*****************************************************/
|
||||
memcpy(password_buf, key, 20);
|
||||
memcpy(password_buf + 20, engineID, engineLength);
|
||||
memcpy(password_buf + 20 + engineLength, key, 20);
|
||||
|
||||
mbedtls_sha1_starts(&SH);
|
||||
mbedtls_sha1_update(&SH, password_buf, 40 + engineLength);
|
||||
mbedtls_sha1_finish(&SH, key);
|
||||
|
||||
mbedtls_sha1_free(&SH);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */
|
||||
1502
ext/lwip/src/core/dns.c
Normal file
1502
ext/lwip/src/core/dns.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,7 @@
|
||||
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
@@ -60,6 +61,7 @@
|
||||
# ifndef LWIP_CHKSUM_ALGORITHM
|
||||
# define LWIP_CHKSUM_ALGORITHM 2
|
||||
# endif
|
||||
u16_t lwip_standard_chksum(const void *dataptr, int len);
|
||||
#endif
|
||||
/* If none set: */
|
||||
#ifndef LWIP_CHKSUM_ALGORITHM
|
||||
@@ -77,16 +79,16 @@
|
||||
* @note accumulator size limits summable length to 64k
|
||||
* @note host endianess is irrelevant (p3 RFC1071)
|
||||
*/
|
||||
static u16_t
|
||||
lwip_standard_chksum(void *dataptr, u16_t len)
|
||||
u16_t
|
||||
lwip_standard_chksum(const void *dataptr, int len)
|
||||
{
|
||||
u32_t acc;
|
||||
u16_t src;
|
||||
u8_t *octetptr;
|
||||
const u8_t *octetptr;
|
||||
|
||||
acc = 0;
|
||||
/* dataptr may be at odd or even addresses */
|
||||
octetptr = (u8_t*)dataptr;
|
||||
octetptr = (const u8_t*)dataptr;
|
||||
while (len > 1) {
|
||||
/* declare first octet as most significant
|
||||
thus assume network order, ignoring host order */
|
||||
@@ -130,12 +132,12 @@ lwip_standard_chksum(void *dataptr, u16_t len)
|
||||
* @param len length of data to be summed
|
||||
* @return host order (!) lwip checksum (non-inverted Internet sum)
|
||||
*/
|
||||
|
||||
static u16_t
|
||||
lwip_standard_chksum(void *dataptr, int len)
|
||||
u16_t
|
||||
lwip_standard_chksum(const void *dataptr, int len)
|
||||
{
|
||||
u8_t *pb = (u8_t *)dataptr;
|
||||
u16_t *ps, t = 0;
|
||||
const u8_t *pb = (const u8_t *)dataptr;
|
||||
const u16_t *ps;
|
||||
u16_t t = 0;
|
||||
u32_t sum = 0;
|
||||
int odd = ((mem_ptr_t)pb & 1);
|
||||
|
||||
@@ -146,7 +148,7 @@ lwip_standard_chksum(void *dataptr, int len)
|
||||
}
|
||||
|
||||
/* Add the bulk of the data */
|
||||
ps = (u16_t *)(void *)pb;
|
||||
ps = (const u16_t *)(const void *)pb;
|
||||
while (len > 1) {
|
||||
sum += *ps++;
|
||||
len -= 2;
|
||||
@@ -154,14 +156,14 @@ lwip_standard_chksum(void *dataptr, int len)
|
||||
|
||||
/* Consume left-over byte, if any */
|
||||
if (len > 0) {
|
||||
((u8_t *)&t)[0] = *(u8_t *)ps;
|
||||
((u8_t *)&t)[0] = *(const u8_t *)ps;
|
||||
}
|
||||
|
||||
/* Add end bytes */
|
||||
sum += t;
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is propably faster than if statements... */
|
||||
calling this twice is probably faster than if statements... */
|
||||
sum = FOLD_U32T(sum);
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
@@ -186,13 +188,13 @@ lwip_standard_chksum(void *dataptr, int len)
|
||||
*
|
||||
* by Curt McDowell, Broadcom Corp. December 8th, 2005
|
||||
*/
|
||||
|
||||
static u16_t
|
||||
lwip_standard_chksum(void *dataptr, int len)
|
||||
u16_t
|
||||
lwip_standard_chksum(const void *dataptr, int len)
|
||||
{
|
||||
u8_t *pb = (u8_t *)dataptr;
|
||||
u16_t *ps, t = 0;
|
||||
u32_t *pl;
|
||||
const u8_t *pb = (const u8_t *)dataptr;
|
||||
const u16_t *ps;
|
||||
u16_t t = 0;
|
||||
const u32_t *pl;
|
||||
u32_t sum = 0, tmp;
|
||||
/* starts at odd byte address? */
|
||||
int odd = ((mem_ptr_t)pb & 1);
|
||||
@@ -202,14 +204,14 @@ lwip_standard_chksum(void *dataptr, int len)
|
||||
len--;
|
||||
}
|
||||
|
||||
ps = (u16_t *)pb;
|
||||
ps = (const u16_t *)(const void*)pb;
|
||||
|
||||
if (((mem_ptr_t)ps & 3) && len > 1) {
|
||||
sum += *ps++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
pl = (u32_t *)ps;
|
||||
pl = (const u32_t *)(const void*)ps;
|
||||
|
||||
while (len > 7) {
|
||||
tmp = sum + *pl++; /* ping */
|
||||
@@ -228,7 +230,7 @@ lwip_standard_chksum(void *dataptr, int len)
|
||||
/* make room in upper bits */
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
ps = (u16_t *)pl;
|
||||
ps = (const u16_t *)pl;
|
||||
|
||||
/* 16-bit aligned word remaining? */
|
||||
while (len > 1) {
|
||||
@@ -238,13 +240,13 @@ lwip_standard_chksum(void *dataptr, int len)
|
||||
|
||||
/* dangling tail byte remaining? */
|
||||
if (len > 0) { /* include odd byte */
|
||||
((u8_t *)&t)[0] = *(u8_t *)ps;
|
||||
((u8_t *)&t)[0] = *(const u8_t *)ps;
|
||||
}
|
||||
|
||||
sum += t; /* add end bytes */
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is propably faster than if statements... */
|
||||
calling this twice is probably faster than if statements... */
|
||||
sum = FOLD_U32T(sum);
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
@@ -256,30 +258,13 @@ lwip_standard_chksum(void *dataptr, int len)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* inet_chksum_pseudo:
|
||||
*
|
||||
* Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* IP addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
inet_chksum_pseudo(struct pbuf *p,
|
||||
ip_addr_t *src, ip_addr_t *dest,
|
||||
u8_t proto, u16_t proto_len)
|
||||
/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
|
||||
static u16_t
|
||||
inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
|
||||
{
|
||||
u32_t acc;
|
||||
u32_t addr;
|
||||
struct pbuf *q;
|
||||
u8_t swapped;
|
||||
u8_t swapped = 0;
|
||||
|
||||
acc = 0;
|
||||
swapped = 0;
|
||||
/* iterate through all pbuf in chain */
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
|
||||
@@ -299,26 +284,22 @@ inet_chksum_pseudo(struct pbuf *p,
|
||||
if (swapped) {
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
addr = ip4_addr_get_u32(src);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = ip4_addr_get_u32(dest);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
|
||||
acc += (u32_t)htons((u16_t)proto);
|
||||
acc += (u32_t)htons(proto_len);
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is propably faster than if statements... */
|
||||
calling this twice is probably faster than if statements... */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
|
||||
return (u16_t)~(acc & 0xffffUL);
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* inet_chksum_pseudo:
|
||||
*
|
||||
* Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* IP addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
@@ -329,18 +310,102 @@ inet_chksum_pseudo(struct pbuf *p,
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
inet_chksum_pseudo_partial(struct pbuf *p,
|
||||
ip_addr_t *src, ip_addr_t *dest,
|
||||
u8_t proto, u16_t proto_len, u16_t chksum_len)
|
||||
inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
const ip4_addr_t *src, const ip4_addr_t *dest)
|
||||
{
|
||||
u32_t acc;
|
||||
u32_t addr;
|
||||
|
||||
addr = ip4_addr_get_u32(src);
|
||||
acc = (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = ip4_addr_get_u32(dest);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_base(p, proto, proto_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
#if LWIP_IPV6
|
||||
/**
|
||||
* Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
|
||||
* IPv6 addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param proto ipv6 protocol/next header (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
|
||||
* @param src source ipv6 address (used for checksum of pseudo header)
|
||||
* @param dest destination ipv6 address (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
const ip6_addr_t *src, const ip6_addr_t *dest)
|
||||
{
|
||||
u32_t acc = 0;
|
||||
u32_t addr;
|
||||
u8_t addr_part;
|
||||
|
||||
for (addr_part = 0; addr_part < 4; addr_part++) {
|
||||
addr = src->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = dest->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
}
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_base(p, proto, proto_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
/* ip_chksum_pseudo:
|
||||
*
|
||||
* Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* IP addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
const ip_addr_t *src, const ip_addr_t *dest)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(dest)) {
|
||||
return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
else
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
#if LWIP_IPV4
|
||||
{
|
||||
return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
}
|
||||
|
||||
/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
|
||||
static u16_t
|
||||
inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, u32_t acc)
|
||||
{
|
||||
struct pbuf *q;
|
||||
u8_t swapped;
|
||||
u8_t swapped = 0;
|
||||
u16_t chklen;
|
||||
|
||||
acc = 0;
|
||||
swapped = 0;
|
||||
/* iterate through all pbuf in chain */
|
||||
for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
|
||||
@@ -365,23 +430,120 @@ inet_chksum_pseudo_partial(struct pbuf *p,
|
||||
if (swapped) {
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
addr = ip4_addr_get_u32(src);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = ip4_addr_get_u32(dest);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
|
||||
acc += (u32_t)htons((u16_t)proto);
|
||||
acc += (u32_t)htons(proto_len);
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is propably faster than if statements... */
|
||||
calling this twice is probably faster than if statements... */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
|
||||
return (u16_t)~(acc & 0xffffUL);
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* inet_chksum_pseudo_partial:
|
||||
*
|
||||
* Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* IP addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest)
|
||||
{
|
||||
u32_t acc;
|
||||
u32_t addr;
|
||||
|
||||
addr = ip4_addr_get_u32(src);
|
||||
acc = (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = ip4_addr_get_u32(dest);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
#if LWIP_IPV6
|
||||
/**
|
||||
* Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
|
||||
* IPv6 addresses are expected to be in network byte order. Will only compute for a
|
||||
* portion of the payload.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param proto ipv6 protocol/next header (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
|
||||
* @param chksum_len number of payload bytes used to compute chksum
|
||||
* @param src source ipv6 address (used for checksum of pseudo header)
|
||||
* @param dest destination ipv6 address (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
|
||||
{
|
||||
u32_t acc = 0;
|
||||
u32_t addr;
|
||||
u8_t addr_part;
|
||||
|
||||
for (addr_part = 0; addr_part < 4; addr_part++) {
|
||||
addr = src->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = dest->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
}
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
/* ip_chksum_pseudo_partial:
|
||||
*
|
||||
* Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(dest)) {
|
||||
return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
else
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
#if LWIP_IPV4
|
||||
{
|
||||
return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
}
|
||||
|
||||
/* inet_chksum:
|
||||
*
|
||||
* Calculates the Internet checksum over a portion of memory. Used primarily for IP
|
||||
@@ -393,9 +555,9 @@ inet_chksum_pseudo_partial(struct pbuf *p,
|
||||
*/
|
||||
|
||||
u16_t
|
||||
inet_chksum(void *dataptr, u16_t len)
|
||||
inet_chksum(const void *dataptr, u16_t len)
|
||||
{
|
||||
return ~LWIP_CHKSUM(dataptr, len);
|
||||
return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,7 +33,27 @@
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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 and @ref sys_prot.\n
|
||||
* You can only use @ref callbackstyle_api in this mode.
|
||||
*
|
||||
* @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 LWIP_TCPIP_CORE_LOCKING.\n
|
||||
* Porting: implement all functions in @ref sys_layer.\n
|
||||
* You can use @ref callbackstyle_api together with \#define tcpip_callback,
|
||||
* and all @ref threadsafe_api.
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
@@ -49,15 +69,20 @@
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp_impl.h"
|
||||
#include "lwip/snmp_msg.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/autoip.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/timers.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "lwip/timeouts.h"
|
||||
#include "lwip/etharp.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/api.h"
|
||||
|
||||
#include "netif/ppp/ppp_opts.h"
|
||||
#include "netif/ppp/ppp_impl.h"
|
||||
|
||||
/* Compile-time sanity checks for configuration errors.
|
||||
* These can be done independently of LWIP_DEBUG, without penalty.
|
||||
*/
|
||||
@@ -70,17 +95,11 @@
|
||||
#if (!LWIP_UDP && LWIP_UDPLITE)
|
||||
#error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_SNMP)
|
||||
#error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_DHCP)
|
||||
#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_IGMP)
|
||||
#error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_SNMP)
|
||||
#error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS)
|
||||
#error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_DNS)
|
||||
#error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
@@ -101,20 +120,44 @@
|
||||
#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
|
||||
#error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS)
|
||||
#error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_IGMP && !LWIP_IPV4)
|
||||
#error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4)
|
||||
#error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h"
|
||||
#endif
|
||||
#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
|
||||
#error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
|
||||
#endif
|
||||
/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
|
||||
#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT))
|
||||
#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)))
|
||||
#error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
|
||||
#endif
|
||||
#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
|
||||
#error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
|
||||
#endif
|
||||
#endif /* !MEMP_MEM_MALLOC */
|
||||
#if (LWIP_TCP && (TCP_WND > 0xffff))
|
||||
#error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
|
||||
#if LWIP_WND_SCALE
|
||||
#if (LWIP_TCP && (TCP_WND > 0xffffffff))
|
||||
#error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && LWIP_WND_SCALE && (TCP_RCV_SCALE > 14))
|
||||
#error "The maximum valid window scale value is 14!"
|
||||
#endif
|
||||
#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE)))
|
||||
#error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!"
|
||||
#endif
|
||||
#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0))
|
||||
#error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!"
|
||||
#endif
|
||||
#else /* LWIP_WND_SCALE */
|
||||
#if (LWIP_TCP && (TCP_WND > 0xffff))
|
||||
#error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)"
|
||||
#endif
|
||||
#endif /* LWIP_WND_SCALE */
|
||||
#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
|
||||
#error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
|
||||
#endif
|
||||
@@ -124,7 +167,7 @@
|
||||
#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
|
||||
#error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))
|
||||
#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)))
|
||||
#error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
|
||||
#endif
|
||||
#if (LWIP_NETIF_API && (NO_SYS==1))
|
||||
@@ -133,8 +176,11 @@
|
||||
#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
|
||||
#error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_NETCONN && LWIP_SOCKET)
|
||||
#error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h"
|
||||
#if (LWIP_PPP_API && (NO_SYS==1))
|
||||
#error "If you want to use PPP API, you have to define NO_SYS=0 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_PPP_API && (PPP_SUPPORT==0))
|
||||
#error "If you want to use PPP API, you have to enable PPP_SUPPORT in your lwipopts.h"
|
||||
#endif
|
||||
#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
|
||||
#error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
|
||||
@@ -145,12 +191,6 @@
|
||||
#if (!LWIP_ARP && LWIP_AUTOIP)
|
||||
#error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0))
|
||||
#error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
|
||||
#error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
|
||||
#error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
|
||||
#endif
|
||||
@@ -166,14 +206,23 @@
|
||||
#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
|
||||
#error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
|
||||
#endif
|
||||
#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT
|
||||
#error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on"
|
||||
#if PPP_SUPPORT && !PPPOS_SUPPORT && !PPPOE_SUPPORT && !PPPOL2TP_SUPPORT
|
||||
#error "PPP_SUPPORT needs at least one of PPPOS_SUPPORT, PPPOE_SUPPORT or PPPOL2TP_SUPPORT turned on"
|
||||
#endif
|
||||
#if PPP_SUPPORT && !PPP_IPV4_SUPPORT && !PPP_IPV6_SUPPORT
|
||||
#error "PPP_SUPPORT needs PPP_IPV4_SUPPORT and/or PPP_IPV6_SUPPORT turned on"
|
||||
#endif
|
||||
#if PPP_SUPPORT && PPP_IPV4_SUPPORT && !LWIP_IPV4
|
||||
#error "PPP_IPV4_SUPPORT needs LWIP_IPV4 turned on"
|
||||
#endif
|
||||
#if PPP_SUPPORT && PPP_IPV6_SUPPORT && !LWIP_IPV6
|
||||
#error "PPP_IPV6_SUPPORT needs LWIP_IPV6 turned on"
|
||||
#endif
|
||||
#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
|
||||
#error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
|
||||
#endif
|
||||
#if LWIP_IGMP && !defined(LWIP_RAND)
|
||||
#error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
|
||||
#if (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND)
|
||||
#error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
|
||||
#endif
|
||||
#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
|
||||
#error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
|
||||
@@ -194,9 +243,6 @@
|
||||
#endif /* LWIP_NETCONN && LWIP_TCP */
|
||||
#if LWIP_SOCKET
|
||||
/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
|
||||
#if SO_ACCEPTCONN != SOF_ACCEPTCONN
|
||||
#error "SO_ACCEPTCONN != SOF_ACCEPTCONN"
|
||||
#endif
|
||||
#if SO_REUSEADDR != SOF_REUSEADDR
|
||||
#error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
|
||||
#endif
|
||||
@@ -206,9 +252,6 @@
|
||||
#if SO_BROADCAST != SOF_BROADCAST
|
||||
#error "WARNING: SO_BROADCAST != SOF_BROADCAST"
|
||||
#endif
|
||||
#if SO_LINGER != SOF_LINGER
|
||||
#error "WARNING: SO_LINGER != SOF_LINGER"
|
||||
#endif
|
||||
#endif /* LWIP_SOCKET */
|
||||
|
||||
|
||||
@@ -217,9 +260,6 @@
|
||||
#ifdef MEMP_NUM_TCPIP_MSG
|
||||
#error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#ifdef MEMP_NUM_API_MSG
|
||||
#error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#ifdef TCP_REXMIT_DEBUG
|
||||
#error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
@@ -232,6 +272,9 @@
|
||||
#ifdef ETHARP_ALWAYS_INSERT
|
||||
#error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#if !NO_SYS && LWIP_TCPIP_CORE_LOCKING && LWIP_COMPAT_MUTEX && !defined(LWIP_COMPAT_MUTEX_ALLOWED)
|
||||
#error "LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)"
|
||||
#endif
|
||||
|
||||
#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
|
||||
#define LWIP_DISABLE_TCP_SANITY_CHECKS 0
|
||||
@@ -241,9 +284,9 @@
|
||||
#endif
|
||||
|
||||
/* MEMP sanity checks */
|
||||
#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
|
||||
#if LWIP_NETCONN
|
||||
#if MEMP_MEM_MALLOC
|
||||
#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
|
||||
#if LWIP_NETCONN || LWIP_SOCKET
|
||||
#if !MEMP_NUM_NETCONN && LWIP_SOCKET
|
||||
#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
|
||||
#endif
|
||||
@@ -251,9 +294,15 @@
|
||||
#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
|
||||
#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#endif /* MEMP_MEM_MALLOC */
|
||||
#endif /* LWIP_NETCONN */
|
||||
#endif /* LWIP_NETCONN || LWIP_SOCKET */
|
||||
#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
|
||||
#if MEM_USE_POOLS
|
||||
#error "MEMP_MEM_MALLOC and MEM_USE_POOLS cannot be enabled at the same time"
|
||||
#endif
|
||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
||||
#error "LWIP_HOOK_MEMP_AVAILABLE doesn't make sense with MEMP_MEM_MALLOC"
|
||||
#endif
|
||||
#endif /* MEMP_MEM_MALLOC */
|
||||
|
||||
/* TCP sanity checks */
|
||||
#if !LWIP_DISABLE_TCP_SANITY_CHECKS
|
||||
@@ -270,10 +319,16 @@
|
||||
#if TCP_SNDLOWAT >= TCP_SND_BUF
|
||||
#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS))
|
||||
#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!"
|
||||
#endif
|
||||
#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
|
||||
#error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
|
||||
#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
|
||||
#error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
|
||||
#error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if TCP_WND < TCP_MSS
|
||||
@@ -283,7 +338,9 @@
|
||||
#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
|
||||
|
||||
/**
|
||||
* Perform Sanity check of user-configurable values, and initialize all modules.
|
||||
* @ingroup lwip_nosys
|
||||
* Initialize all modules.
|
||||
* Use this in NO_SYS mode. Use tcpip_init() otherwise.
|
||||
*/
|
||||
void
|
||||
lwip_init(void)
|
||||
@@ -297,13 +354,12 @@ lwip_init(void)
|
||||
memp_init();
|
||||
pbuf_init();
|
||||
netif_init();
|
||||
#if LWIP_SOCKET
|
||||
//lwip_socket_init();
|
||||
#endif /* LWIP_SOCKET */
|
||||
#if LWIP_IPV4
|
||||
ip_init();
|
||||
#if LWIP_ARP
|
||||
etharp_init();
|
||||
#endif /* LWIP_ARP */
|
||||
#endif /* LWIP_IPV4 */
|
||||
#if LWIP_RAW
|
||||
raw_init();
|
||||
#endif /* LWIP_RAW */
|
||||
@@ -313,9 +369,6 @@ lwip_init(void)
|
||||
#if LWIP_TCP
|
||||
tcp_init();
|
||||
#endif /* LWIP_TCP */
|
||||
#if LWIP_SNMP
|
||||
snmp_init();
|
||||
#endif /* LWIP_SNMP */
|
||||
#if LWIP_AUTOIP
|
||||
autoip_init();
|
||||
#endif /* LWIP_AUTOIP */
|
||||
@@ -325,6 +378,9 @@ lwip_init(void)
|
||||
#if LWIP_DNS
|
||||
dns_init();
|
||||
#endif /* LWIP_DNS */
|
||||
#if PPP_SUPPORT
|
||||
ppp_init();
|
||||
#endif
|
||||
|
||||
#if LWIP_TIMERS
|
||||
sys_timeouts_init();
|
||||
125
ext/lwip/src/core/ip.c
Normal file
125
ext/lwip/src/core/ip.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @file ip.c
|
||||
* Common IPv4 and IPv6 code
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ip4 IPv4
|
||||
* @ingroup callbackstyle_api
|
||||
*
|
||||
* @defgroup ip6 IPv6
|
||||
* @ingroup callbackstyle_api
|
||||
*
|
||||
* @defgroup ipaddr IP address handling
|
||||
* @ingroup infrastructure
|
||||
*
|
||||
* @defgroup ip4addr IPv4 only
|
||||
* @ingroup ipaddr
|
||||
*
|
||||
* @defgroup ip6addr IPv6 only
|
||||
* @ingroup ipaddr
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4 || LWIP_IPV6
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/ip.h"
|
||||
|
||||
/** Global data for both IPv4 and IPv6 */
|
||||
struct ip_globals ip_data;
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
|
||||
const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT;
|
||||
|
||||
/**
|
||||
* @ingroup ipaddr
|
||||
* Convert IP address string (both versions) to numeric.
|
||||
* The version is auto-detected from the string.
|
||||
*
|
||||
* @param cp IP address string to convert
|
||||
* @param addr conversion result is stored here
|
||||
* @return 1 on success, 0 on error
|
||||
*/
|
||||
int
|
||||
ipaddr_aton(const char *cp, ip_addr_t *addr)
|
||||
{
|
||||
if (cp != NULL) {
|
||||
const char* c;
|
||||
for (c = cp; *c != 0; c++) {
|
||||
if (*c == ':') {
|
||||
/* contains a colon: IPv6 address */
|
||||
if (addr) {
|
||||
IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V6);
|
||||
}
|
||||
return ip6addr_aton(cp, ip_2_ip6(addr));
|
||||
} else if (*c == '.') {
|
||||
/* contains a dot: IPv4 address */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* call ip4addr_aton as fallback or if IPv4 was found */
|
||||
if (addr) {
|
||||
IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V4);
|
||||
}
|
||||
return ip4addr_aton(cp, ip_2_ip4(addr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup lwip_nosys
|
||||
* If both IP versions are enabled, this function can dispatch packets to the correct one.
|
||||
* Don't call directly, pass to netif_add() and call netif->input().
|
||||
*/
|
||||
err_t
|
||||
ip_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
LWIP_DEBUGF(IP6_DEBUG, ("ip_input: netif = %p\n", (void*)inp));
|
||||
if (p != NULL) {
|
||||
if (IP_HDR_GET_VERSION(p->payload) == 6) {
|
||||
return ip6_input(p, inp);
|
||||
}
|
||||
return ip4_input(p, inp);
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
|
||||
#endif /* LWIP_IPV4 || LWIP_IPV6 */
|
||||
@@ -62,16 +62,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup autoip AUTOIP
|
||||
* @ingroup ip4
|
||||
* AUTOIP related functions
|
||||
* @see netifapi_autoip
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
|
||||
#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/udp.h"
|
||||
/* #include "lwip/udp.h" */
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/autoip.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "lwip/etharp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -83,6 +90,23 @@
|
||||
/* 169.254.254.255 */
|
||||
#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF)
|
||||
|
||||
/* RFC 3927 Constants */
|
||||
#define PROBE_WAIT 1 /* second (initial random delay) */
|
||||
#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */
|
||||
#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */
|
||||
#define PROBE_NUM 3 /* (number of probe packets) */
|
||||
#define ANNOUNCE_NUM 2 /* (number of announcement packets) */
|
||||
#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */
|
||||
#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */
|
||||
#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */
|
||||
#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */
|
||||
#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */
|
||||
|
||||
/* AutoIP client states */
|
||||
#define AUTOIP_STATE_OFF 0
|
||||
#define AUTOIP_STATE_PROBING 1
|
||||
#define AUTOIP_STATE_ANNOUNCING 2
|
||||
#define AUTOIP_STATE_BOUND 3
|
||||
|
||||
/** Pseudo random macro based on netif informations.
|
||||
* You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
|
||||
@@ -105,29 +129,17 @@
|
||||
#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
|
||||
|
||||
/* static functions */
|
||||
static void autoip_handle_arp_conflict(struct netif *netif);
|
||||
|
||||
/* creates a pseudo random LL IP-Address for a network interface */
|
||||
static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr);
|
||||
|
||||
/* sends an ARP probe */
|
||||
static err_t autoip_arp_probe(struct netif *netif);
|
||||
|
||||
/* sends an ARP announce */
|
||||
static err_t autoip_arp_announce(struct netif *netif);
|
||||
|
||||
/* configure interface for use with current LL IP-Address */
|
||||
static err_t autoip_bind(struct netif *netif);
|
||||
|
||||
/* start sending probes for llipaddr */
|
||||
static void autoip_start_probing(struct netif *netif);
|
||||
|
||||
|
||||
/** Set a statically allocated struct autoip to work with.
|
||||
/**
|
||||
* @ingroup autoip
|
||||
* Set a statically allocated struct autoip to work with.
|
||||
* Using this prevents autoip_start to allocate it using mem_malloc.
|
||||
*
|
||||
* @param netif the netif for which to set the struct autoip
|
||||
* @param dhcp (uninitialised) dhcp struct allocated by the application
|
||||
* @param autoip (uninitialised) autoip struct allocated by the application
|
||||
*/
|
||||
void
|
||||
autoip_set_struct(struct netif *netif, struct autoip *autoip)
|
||||
@@ -159,18 +171,19 @@ autoip_restart(struct netif *netif)
|
||||
static void
|
||||
autoip_handle_arp_conflict(struct netif *netif)
|
||||
{
|
||||
/* Somehow detect if we are defending or retreating */
|
||||
unsigned char defend = 1; /* tbd */
|
||||
/* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where
|
||||
a) means retreat on the first conflict and
|
||||
b) allows to keep an already configured address when having only one
|
||||
conflict in 10 seconds
|
||||
We use option b) since it helps to improve the chance that one of the two
|
||||
conflicting hosts may be able to retain its address. */
|
||||
|
||||
if (defend) {
|
||||
if (netif->autoip->lastconflict > 0) {
|
||||
/* retreat, there was a conflicting ARP in the last
|
||||
* DEFEND_INTERVAL seconds
|
||||
*/
|
||||
/* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
|
||||
|
||||
/* TODO: close all TCP sessions */
|
||||
/* Active TCP sessions are aborted when removing the ip addresss */
|
||||
autoip_restart(netif);
|
||||
} else {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
@@ -178,12 +191,6 @@ autoip_handle_arp_conflict(struct netif *netif)
|
||||
autoip_arp_announce(netif);
|
||||
netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
|
||||
}
|
||||
} else {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_handle_arp_conflict(): we do not defend, retreating\n"));
|
||||
/* TODO: close all TCP sessions */
|
||||
autoip_restart(netif);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +200,7 @@ autoip_handle_arp_conflict(struct netif *netif)
|
||||
* @param ipaddr ip address to initialize
|
||||
*/
|
||||
static void
|
||||
autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
|
||||
autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
|
||||
{
|
||||
/* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
|
||||
* compliant to RFC 3927 Section 2.1
|
||||
@@ -228,9 +235,8 @@ autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
|
||||
static err_t
|
||||
autoip_arp_probe(struct netif *netif)
|
||||
{
|
||||
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
|
||||
(struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero,
|
||||
&netif->autoip->llipaddr, ARP_REQUEST);
|
||||
/* this works because netif->ip_addr is ANY */
|
||||
return etharp_request(netif, &netif->autoip->llipaddr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,9 +247,7 @@ autoip_arp_probe(struct netif *netif)
|
||||
static err_t
|
||||
autoip_arp_announce(struct netif *netif)
|
||||
{
|
||||
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
|
||||
(struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero,
|
||||
&netif->autoip->llipaddr, ARP_REQUEST);
|
||||
return etharp_gratuitous(netif);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +259,7 @@ static err_t
|
||||
autoip_bind(struct netif *netif)
|
||||
{
|
||||
struct autoip *autoip = netif->autoip;
|
||||
ip_addr_t sn_mask, gw_addr;
|
||||
ip4_addr_t sn_mask, gw_addr;
|
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
|
||||
("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
@@ -266,17 +270,14 @@ autoip_bind(struct netif *netif)
|
||||
IP4_ADDR(&sn_mask, 255, 255, 0, 0);
|
||||
IP4_ADDR(&gw_addr, 0, 0, 0, 0);
|
||||
|
||||
netif_set_ipaddr(netif, &autoip->llipaddr);
|
||||
netif_set_netmask(netif, &sn_mask);
|
||||
netif_set_gw(netif, &gw_addr);
|
||||
|
||||
/* bring the interface up */
|
||||
netif_set_up(netif);
|
||||
netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
|
||||
/* interface is used by routing now that an address is set */
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup autoip
|
||||
* Start AutoIP client
|
||||
*
|
||||
* @param netif network interface on which start the AutoIP client
|
||||
@@ -287,16 +288,12 @@ autoip_start(struct netif *netif)
|
||||
struct autoip *autoip = netif->autoip;
|
||||
err_t result = ERR_OK;
|
||||
|
||||
if (netif_is_up(netif)) {
|
||||
netif_set_down(netif);
|
||||
}
|
||||
LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
|
||||
|
||||
/* Set IP-Address, Netmask and Gateway to 0 to make sure that
|
||||
* ARP Packets are formed correctly
|
||||
*/
|
||||
ip_addr_set_zero(&netif->ip_addr);
|
||||
ip_addr_set_zero(&netif->netmask);
|
||||
ip_addr_set_zero(&netif->gw);
|
||||
netif_set_addr(netif, IP4_ADDR_ANY, IP4_ADDR_ANY, IP4_ADDR_ANY);
|
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
|
||||
@@ -319,7 +316,7 @@ autoip_start(struct netif *netif)
|
||||
autoip->state = AUTOIP_STATE_OFF;
|
||||
autoip->ttw = 0;
|
||||
autoip->sent_num = 0;
|
||||
ip_addr_set_zero(&autoip->llipaddr);
|
||||
ip4_addr_set_zero(&autoip->llipaddr);
|
||||
autoip->lastconflict = 0;
|
||||
}
|
||||
|
||||
@@ -342,14 +339,14 @@ autoip_start_probing(struct netif *netif)
|
||||
ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
|
||||
|
||||
/* time to wait to first probe, this is randomly
|
||||
* choosen out of 0 to PROBE_WAIT seconds.
|
||||
* chosen out of 0 to PROBE_WAIT seconds.
|
||||
* compliant to RFC 3927 Section 2.2.1
|
||||
*/
|
||||
autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
|
||||
|
||||
/*
|
||||
* if we tried more then MAX_CONFLICTS we must limit our rate for
|
||||
* accquiring and probing address
|
||||
* acquiring and probing address
|
||||
* compliant to RFC 3927 Section 2.2.1
|
||||
*/
|
||||
if (autoip->tried_llipaddr > MAX_CONFLICTS) {
|
||||
@@ -367,12 +364,12 @@ void
|
||||
autoip_network_changed(struct netif *netif)
|
||||
{
|
||||
if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) {
|
||||
netif_set_down(netif);
|
||||
autoip_start_probing(netif);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup autoip
|
||||
* Stop AutoIP client
|
||||
*
|
||||
* @param netif network interface on which stop the AutoIP client
|
||||
@@ -380,8 +377,12 @@ autoip_network_changed(struct netif *netif)
|
||||
err_t
|
||||
autoip_stop(struct netif *netif)
|
||||
{
|
||||
if (netif->autoip) {
|
||||
netif->autoip->state = AUTOIP_STATE_OFF;
|
||||
netif_set_down(netif);
|
||||
if (ip4_addr_islinklocal(netif_ip4_addr(netif))) {
|
||||
netif_set_addr(netif, IP4_ADDR_ANY, IP4_ADDR_ANY, IP4_ADDR_ANY);
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -389,7 +390,7 @@ autoip_stop(struct netif *netif)
|
||||
* Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
|
||||
*/
|
||||
void
|
||||
autoip_tmr()
|
||||
autoip_tmr(void)
|
||||
{
|
||||
struct netif *netif = netif_list;
|
||||
/* loop through netif's */
|
||||
@@ -404,14 +405,20 @@ autoip_tmr()
|
||||
("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
|
||||
(u16_t)(netif->autoip->state), netif->autoip->ttw));
|
||||
|
||||
switch(netif->autoip->state) {
|
||||
case AUTOIP_STATE_PROBING:
|
||||
if (netif->autoip->ttw > 0) {
|
||||
netif->autoip->ttw--;
|
||||
} else {
|
||||
}
|
||||
|
||||
switch(netif->autoip->state) {
|
||||
case AUTOIP_STATE_PROBING:
|
||||
if (netif->autoip->ttw == 0) {
|
||||
if (netif->autoip->sent_num >= PROBE_NUM) {
|
||||
/* Switch to ANNOUNCING: now we can bind to an IP address and use it */
|
||||
netif->autoip->state = AUTOIP_STATE_ANNOUNCING;
|
||||
netif->autoip->sent_num = 0;
|
||||
autoip_bind(netif);
|
||||
/* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP
|
||||
which counts as an announcement */
|
||||
netif->autoip->sent_num = 1;
|
||||
netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
@@ -419,34 +426,25 @@ autoip_tmr()
|
||||
ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
|
||||
} else {
|
||||
autoip_arp_probe(netif);
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
|
||||
("autoip_tmr() PROBING Sent Probe\n"));
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n"));
|
||||
netif->autoip->sent_num++;
|
||||
if (netif->autoip->sent_num == PROBE_NUM) {
|
||||
/* calculate time to wait to for announce */
|
||||
netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
|
||||
} else {
|
||||
/* calculate time to wait to next probe */
|
||||
netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
|
||||
((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
|
||||
PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AUTOIP_STATE_ANNOUNCING:
|
||||
if (netif->autoip->ttw > 0) {
|
||||
netif->autoip->ttw--;
|
||||
} else {
|
||||
if (netif->autoip->sent_num == 0) {
|
||||
/* We are here the first time, so we waited ANNOUNCE_WAIT seconds
|
||||
* Now we can bind to an IP address and use it.
|
||||
*
|
||||
* autoip_bind calls netif_set_up. This triggers a gratuitous ARP
|
||||
* which counts as an announcement.
|
||||
*/
|
||||
autoip_bind(netif);
|
||||
} else {
|
||||
if (netif->autoip->ttw == 0) {
|
||||
autoip_arp_announce(netif);
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
|
||||
("autoip_tmr() ANNOUNCING Sent Announce\n"));
|
||||
}
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n"));
|
||||
netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
|
||||
netif->autoip->sent_num++;
|
||||
|
||||
@@ -461,6 +459,10 @@ autoip_tmr()
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* nothing to do in other states */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
@@ -484,27 +486,25 @@ autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
|
||||
* when probing ip.dst == llipaddr && hw.src != netif->hwaddr
|
||||
* we have a conflict and must solve it
|
||||
*/
|
||||
ip_addr_t sipaddr, dipaddr;
|
||||
ip4_addr_t sipaddr, dipaddr;
|
||||
struct eth_addr netifaddr;
|
||||
ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
|
||||
|
||||
/* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
|
||||
/* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
|
||||
* structure packing (not using structure copy which breaks strict-aliasing rules).
|
||||
*/
|
||||
IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
|
||||
IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
|
||||
|
||||
if ((netif->autoip->state == AUTOIP_STATE_PROBING) ||
|
||||
((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) &&
|
||||
(netif->autoip->sent_num == 0))) {
|
||||
if (netif->autoip->state == AUTOIP_STATE_PROBING) {
|
||||
/* RFC 3927 Section 2.2.1:
|
||||
* from beginning to after ANNOUNCE_WAIT
|
||||
* seconds we have a conflict if
|
||||
* ip.src == llipaddr OR
|
||||
* ip.dst == llipaddr && hw.src != own hwaddr
|
||||
*/
|
||||
if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
|
||||
(ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
|
||||
if ((ip4_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
|
||||
(ip4_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
|
||||
!eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
|
||||
("autoip_arp_reply(): Probe Conflict detected\n"));
|
||||
@@ -515,7 +515,7 @@ autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
|
||||
* in any state we have a conflict if
|
||||
* ip.src == llipaddr && hw.src != own hwaddr
|
||||
*/
|
||||
if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
|
||||
if (ip4_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
|
||||
!eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
|
||||
("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
|
||||
@@ -525,4 +525,22 @@ autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_AUTOIP */
|
||||
/** check if AutoIP supplied netif->ip_addr
|
||||
*
|
||||
* @param netif the netif to check
|
||||
* @return 1 if AutoIP supplied netif->ip_addr (state BOUND or ANNOUNCING),
|
||||
* 0 otherwise
|
||||
*/
|
||||
u8_t
|
||||
autoip_supplied_address(const struct netif *netif)
|
||||
{
|
||||
if ((netif != NULL) && (netif->autoip != NULL)) {
|
||||
if ((netif->autoip->state == AUTOIP_STATE_BOUND) ||
|
||||
(netif->autoip->state == AUTOIP_STATE_ANNOUNCING)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_AUTOIP */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -47,48 +47,29 @@
|
||||
|
||||
#if LWIP_ARP || LWIP_ETHERNET
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/etharp.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/dhcp.h"
|
||||
#include "lwip/autoip.h"
|
||||
#include "netif/etharp.h"
|
||||
|
||||
#if PPPOE_SUPPORT
|
||||
#include "netif/ppp_oe.h"
|
||||
#endif /* PPPOE_SUPPORT */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
|
||||
const struct eth_addr ethzero = {{0,0,0,0,0,0}};
|
||||
#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
/** The 24-bit IANA multicast OUI is 01-00-5e: */
|
||||
#define LL_MULTICAST_ADDR_0 0x01
|
||||
#define LL_MULTICAST_ADDR_1 0x00
|
||||
#define LL_MULTICAST_ADDR_2 0x5e
|
||||
|
||||
#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
/** the time an ARP entry stays valid after its last update,
|
||||
* for ARP_TMR_INTERVAL = 5000, this is
|
||||
* (240 * 5) seconds = 20 minutes.
|
||||
*/
|
||||
#define ARP_MAXAGE 240
|
||||
/** Re-request a used ARP entry 1 minute before it would expire to prevent
|
||||
* breaking a steadily used connection because the ARP entry timed out. */
|
||||
#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12)
|
||||
#define ARP_AGE_REREQUEST_USED_UNICAST (ARP_MAXAGE - 30)
|
||||
#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
|
||||
|
||||
/** the time an ARP entry stays pending after first request,
|
||||
* for ARP_TMR_INTERVAL = 5000, this is
|
||||
* (2 * 5) seconds = 10 seconds.
|
||||
* for ARP_TMR_INTERVAL = 1000, this is
|
||||
* 10 seconds.
|
||||
*
|
||||
* @internal Keep this number at least 2, otherwise it might
|
||||
* run out instantly if the timeout occurs directly after a request.
|
||||
*/
|
||||
#define ARP_MAXPENDING 2
|
||||
#define ARP_MAXPENDING 5
|
||||
|
||||
#define HWTYPE_ETHERNET 1
|
||||
|
||||
@@ -96,7 +77,8 @@ enum etharp_state {
|
||||
ETHARP_STATE_EMPTY = 0,
|
||||
ETHARP_STATE_PENDING,
|
||||
ETHARP_STATE_STABLE,
|
||||
ETHARP_STATE_STABLE_REREQUESTING
|
||||
ETHARP_STATE_STABLE_REREQUESTING_1,
|
||||
ETHARP_STATE_STABLE_REREQUESTING_2
|
||||
#if ETHARP_SUPPORT_STATIC_ENTRIES
|
||||
,ETHARP_STATE_STATIC
|
||||
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
|
||||
@@ -110,11 +92,11 @@ struct etharp_entry {
|
||||
/** Pointer to a single pending outgoing packet on this ARP entry. */
|
||||
struct pbuf *q;
|
||||
#endif /* ARP_QUEUEING */
|
||||
ip_addr_t ipaddr;
|
||||
ip4_addr_t ipaddr;
|
||||
struct netif *netif;
|
||||
struct eth_addr ethaddr;
|
||||
u16_t ctime;
|
||||
u8_t state;
|
||||
u8_t ctime;
|
||||
};
|
||||
|
||||
static struct etharp_entry arp_table[ARP_TABLE_SIZE];
|
||||
@@ -145,6 +127,9 @@ static u8_t etharp_cached_entry;
|
||||
#endif
|
||||
|
||||
|
||||
static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr);
|
||||
|
||||
|
||||
#if ARP_QUEUEING
|
||||
/**
|
||||
* Free a complete queue of etharp entries
|
||||
@@ -177,7 +162,7 @@ static void
|
||||
etharp_free_entry(int i)
|
||||
{
|
||||
/* remove from SNMP ARP index tree */
|
||||
snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
|
||||
mib2_remove_arp_entry(arp_table[i].netif, &arp_table[i].ipaddr);
|
||||
/* and empty packet queue */
|
||||
if (arp_table[i].q != NULL) {
|
||||
/* remove all queued packets */
|
||||
@@ -191,7 +176,7 @@ etharp_free_entry(int i)
|
||||
/* for debugging, clean out the complete entry */
|
||||
arp_table[i].ctime = 0;
|
||||
arp_table[i].netif = NULL;
|
||||
ip_addr_set_zero(&arp_table[i].ipaddr);
|
||||
ip4_addr_set_zero(&arp_table[i].ipaddr);
|
||||
arp_table[i].ethaddr = ethzero;
|
||||
#endif /* LWIP_DEBUG */
|
||||
}
|
||||
@@ -199,7 +184,7 @@ etharp_free_entry(int i)
|
||||
/**
|
||||
* Clears expired entries in the ARP table.
|
||||
*
|
||||
* This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds),
|
||||
* This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
|
||||
* in order to expire entries in the ARP table.
|
||||
*/
|
||||
void
|
||||
@@ -225,18 +210,17 @@ etharp_tmr(void)
|
||||
arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
|
||||
/* clean up entries that have just been expired */
|
||||
etharp_free_entry(i);
|
||||
}
|
||||
else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) {
|
||||
} else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
|
||||
/* Don't send more than one request every 2 seconds. */
|
||||
arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
|
||||
} else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
|
||||
/* Reset state to stable, so that the next transmitted packet will
|
||||
re-send an ARP request. */
|
||||
arp_table[i].state = ETHARP_STATE_STABLE;
|
||||
} else if (arp_table[i].state == ETHARP_STATE_PENDING) {
|
||||
/* still pending, resend an ARP query */
|
||||
etharp_request(arp_table[i].netif, &arp_table[i].ipaddr);
|
||||
}
|
||||
#if ARP_QUEUEING
|
||||
/* still pending entry? (not expired) */
|
||||
if (arp_table[i].state == ETHARP_STATE_PENDING) {
|
||||
/* resend an ARP query here? */
|
||||
}
|
||||
#endif /* ARP_QUEUEING */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,15 +247,17 @@ etharp_tmr(void)
|
||||
* entry is found or could be recycled.
|
||||
*/
|
||||
static s8_t
|
||||
etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
|
||||
etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
|
||||
{
|
||||
s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
|
||||
s8_t empty = ARP_TABLE_SIZE;
|
||||
u8_t i = 0, age_pending = 0, age_stable = 0;
|
||||
u8_t i = 0;
|
||||
/* oldest entry with packets on queue */
|
||||
s8_t old_queue = ARP_TABLE_SIZE;
|
||||
/* its age */
|
||||
u8_t age_queue = 0;
|
||||
u16_t age_queue = 0, age_pending = 0, age_stable = 0;
|
||||
|
||||
LWIP_UNUSED_ARG(netif);
|
||||
|
||||
/**
|
||||
* a) do a search through the cache, remember candidates
|
||||
@@ -299,7 +285,11 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
|
||||
LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
|
||||
state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
|
||||
/* if given, does IP address match IP address in ARP entry? */
|
||||
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
|
||||
if (ipaddr && ip4_addr_cmp(ipaddr, &arp_table[i].ipaddr)
|
||||
#if ETHARP_TABLE_MATCH_NETIF
|
||||
&& ((netif == NULL) || (netif == arp_table[i].netif))
|
||||
#endif /* ETHARP_TABLE_MATCH_NETIF */
|
||||
) {
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
|
||||
/* found exact IP address match, simply bail out */
|
||||
return i;
|
||||
@@ -395,9 +385,12 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
|
||||
/* IP address given? */
|
||||
if (ipaddr != NULL) {
|
||||
/* set IP address */
|
||||
ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
|
||||
ip4_addr_copy(arp_table[i].ipaddr, *ipaddr);
|
||||
}
|
||||
arp_table[i].ctime = 0;
|
||||
#if ETHARP_TABLE_MATCH_NETIF
|
||||
arp_table[i].netif = netif;
|
||||
#endif /* ETHARP_TABLE_MATCH_NETIF*/
|
||||
return (err_t)i;
|
||||
}
|
||||
|
||||
@@ -412,15 +405,31 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
|
||||
* @return ERR_OK if the packet was sent, any other err_t on failure
|
||||
*/
|
||||
static err_t
|
||||
etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
|
||||
etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, const struct eth_addr *dst)
|
||||
{
|
||||
struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
struct eth_vlan_hdr *vlanhdr;
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
|
||||
LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
|
||||
(netif->hwaddr_len == ETHARP_HWADDR_LEN));
|
||||
LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
|
||||
(netif->hwaddr_len == ETH_HWADDR_LEN));
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
ethhdr->type = PP_HTONS(ETHTYPE_VLAN);
|
||||
vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR);
|
||||
vlanhdr->prio_vid = 0;
|
||||
vlanhdr->tpid = PP_HTONS(ETHTYPE_IP);
|
||||
if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) {
|
||||
/* packet shall not contain VLAN header, so hide it and set correct ethertype */
|
||||
pbuf_header(p, -SIZEOF_VLAN_HDR);
|
||||
ethhdr = (struct eth_hdr *)p->payload;
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
ethhdr->type = PP_HTONS(ETHTYPE_IP);
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
}
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
ETHADDR32_COPY(ðhdr->dest, dst);
|
||||
ETHADDR16_COPY(ðhdr->src, src);
|
||||
ethhdr->type = PP_HTONS(ETHTYPE_IP);
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
|
||||
/* send the packet */
|
||||
return netif->linkoutput(netif, p);
|
||||
@@ -438,30 +447,30 @@ etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct
|
||||
* @param flags @see definition of ETHARP_FLAG_*
|
||||
*
|
||||
* @return
|
||||
* - ERR_OK Succesfully updated ARP cache.
|
||||
* - ERR_OK Successfully updated ARP cache.
|
||||
* - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
|
||||
* - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
|
||||
*
|
||||
* @see pbuf_free()
|
||||
*/
|
||||
static err_t
|
||||
etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
|
||||
etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
|
||||
{
|
||||
s8_t i;
|
||||
LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
|
||||
LWIP_ASSERT("netif->hwaddr_len == ETH_HWADDR_LEN", netif->hwaddr_len == ETH_HWADDR_LEN);
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
|
||||
ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
|
||||
ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
|
||||
ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
|
||||
(u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
|
||||
(u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
|
||||
/* non-unicast address? */
|
||||
if (ip_addr_isany(ipaddr) ||
|
||||
ip_addr_isbroadcast(ipaddr, netif) ||
|
||||
ip_addr_ismulticast(ipaddr)) {
|
||||
if (ip4_addr_isany(ipaddr) ||
|
||||
ip4_addr_isbroadcast(ipaddr, netif) ||
|
||||
ip4_addr_ismulticast(ipaddr)) {
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
|
||||
return ERR_ARG;
|
||||
}
|
||||
/* find or create ARP entry */
|
||||
i = etharp_find_entry(ipaddr, flags);
|
||||
i = etharp_find_entry(ipaddr, flags, netif);
|
||||
/* bail out if no entry could be found */
|
||||
if (i < 0) {
|
||||
return (err_t)i;
|
||||
@@ -471,6 +480,9 @@ etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr
|
||||
if (flags & ETHARP_FLAG_STATIC_ENTRY) {
|
||||
/* record static type */
|
||||
arp_table[i].state = ETHARP_STATE_STATIC;
|
||||
} else if (arp_table[i].state == ETHARP_STATE_STATIC) {
|
||||
/* found entry is a static type, don't overwrite it */
|
||||
return ERR_VAL;
|
||||
} else
|
||||
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
|
||||
{
|
||||
@@ -481,7 +493,7 @@ etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr
|
||||
/* record network interface */
|
||||
arp_table[i].netif = netif;
|
||||
/* insert in SNMP ARP index tree */
|
||||
snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
|
||||
mib2_add_arp_entry(netif, &arp_table[i].ipaddr);
|
||||
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
|
||||
/* update address */
|
||||
@@ -523,15 +535,15 @@ etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr
|
||||
* @return @see return values of etharp_add_static_entry
|
||||
*/
|
||||
err_t
|
||||
etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
|
||||
etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr)
|
||||
{
|
||||
struct netif *netif;
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
|
||||
ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
|
||||
ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
|
||||
ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
|
||||
(u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
|
||||
(u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
|
||||
|
||||
netif = ip_route(ipaddr);
|
||||
netif = ip4_route(ipaddr);
|
||||
if (netif == NULL) {
|
||||
return ERR_RTE;
|
||||
}
|
||||
@@ -548,14 +560,14 @@ etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
|
||||
* ERR_ARG: entry wasn't a static entry but a dynamic one
|
||||
*/
|
||||
err_t
|
||||
etharp_remove_static_entry(ip_addr_t *ipaddr)
|
||||
etharp_remove_static_entry(const ip4_addr_t *ipaddr)
|
||||
{
|
||||
s8_t i;
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
|
||||
|
||||
/* find or create ARP entry */
|
||||
i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
|
||||
i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL);
|
||||
/* bail out if no entry could be found */
|
||||
if (i < 0) {
|
||||
return (err_t)i;
|
||||
@@ -576,7 +588,8 @@ etharp_remove_static_entry(ip_addr_t *ipaddr)
|
||||
*
|
||||
* @param netif points to a network interface
|
||||
*/
|
||||
void etharp_cleanup_netif(struct netif *netif)
|
||||
void
|
||||
etharp_cleanup_netif(struct netif *netif)
|
||||
{
|
||||
u8_t i;
|
||||
|
||||
@@ -600,8 +613,8 @@ void etharp_cleanup_netif(struct netif *netif)
|
||||
* @return table index if found, -1 otherwise
|
||||
*/
|
||||
s8_t
|
||||
etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
|
||||
struct eth_addr **eth_ret, ip_addr_t **ip_ret)
|
||||
etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr,
|
||||
struct eth_addr **eth_ret, const ip4_addr_t **ip_ret)
|
||||
{
|
||||
s8_t i;
|
||||
|
||||
@@ -610,7 +623,7 @@ etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
|
||||
|
||||
LWIP_UNUSED_ARG(netif);
|
||||
|
||||
i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
|
||||
i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif);
|
||||
if ((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
|
||||
*eth_ret = &arp_table[i].ethaddr;
|
||||
*ip_ret = &arp_table[i].ipaddr;
|
||||
@@ -619,6 +632,32 @@ etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibility to iterate over stable ARP table entries
|
||||
*
|
||||
* @param i entry number, 0 to ARP_TABLE_SIZE
|
||||
* @param ipaddr return value: IP address
|
||||
* @param netif return value: points to interface
|
||||
* @param eth_ret return value: ETH address
|
||||
* @return 1 on valid index, 0 otherwise
|
||||
*/
|
||||
u8_t
|
||||
etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret)
|
||||
{
|
||||
LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
|
||||
LWIP_ASSERT("netif != NULL", netif != NULL);
|
||||
LWIP_ASSERT("eth_ret != NULL", eth_ret != NULL);
|
||||
|
||||
if((i < ARP_TABLE_SIZE) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
|
||||
*ipaddr = &arp_table[i].ipaddr;
|
||||
*netif = arp_table[i].netif;
|
||||
*eth_ret = &arp_table[i].ethaddr;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if ETHARP_TRUST_IP_MAC
|
||||
/**
|
||||
* Updates the ARP table using the given IP packet.
|
||||
@@ -635,12 +674,12 @@ etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
|
||||
*
|
||||
* @see pbuf_free()
|
||||
*/
|
||||
static void
|
||||
void
|
||||
etharp_ip_input(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
struct eth_hdr *ethhdr;
|
||||
struct ip_hdr *iphdr;
|
||||
ip_addr_t iphdr_src;
|
||||
ip4_addr_t iphdr_src;
|
||||
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
|
||||
|
||||
/* Only insert an entry if the source IP address of the
|
||||
@@ -653,10 +692,10 @@ etharp_ip_input(struct netif *netif, struct pbuf *p)
|
||||
}
|
||||
#endif /* ETHARP_SUPPORT_VLAN */
|
||||
|
||||
ip_addr_copy(iphdr_src, iphdr->src);
|
||||
ip4_addr_copy(iphdr_src, iphdr->src);
|
||||
|
||||
/* source is not on the local network? */
|
||||
if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
|
||||
if (!ip4_addr_netcmp(&iphdr_src, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {
|
||||
/* do nothing */
|
||||
return;
|
||||
}
|
||||
@@ -684,13 +723,13 @@ etharp_ip_input(struct netif *netif, struct pbuf *p)
|
||||
*
|
||||
* @see pbuf_free()
|
||||
*/
|
||||
static void
|
||||
void
|
||||
etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
|
||||
{
|
||||
struct etharp_hdr *hdr;
|
||||
struct eth_hdr *ethhdr;
|
||||
/* these are aligned properly, whereas the ARP header fields might not be */
|
||||
ip_addr_t sipaddr, dipaddr;
|
||||
ip4_addr_t sipaddr, dipaddr;
|
||||
u8_t for_us;
|
||||
#if LWIP_AUTOIP
|
||||
const u8_t * ethdst_hwaddr;
|
||||
@@ -720,12 +759,12 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
|
||||
|
||||
/* RFC 826 "Packet Reception": */
|
||||
if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
|
||||
(hdr->hwlen != ETHARP_HWADDR_LEN) ||
|
||||
(hdr->protolen != sizeof(ip_addr_t)) ||
|
||||
(hdr->hwlen != ETH_HWADDR_LEN) ||
|
||||
(hdr->protolen != sizeof(ip4_addr_t)) ||
|
||||
(hdr->proto != PP_HTONS(ETHTYPE_IP))) {
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
|
||||
("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
|
||||
hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
|
||||
hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
|
||||
ETHARP_STATS_INC(etharp.proterr);
|
||||
ETHARP_STATS_INC(etharp.drop);
|
||||
pbuf_free(p);
|
||||
@@ -735,22 +774,22 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
|
||||
|
||||
#if LWIP_AUTOIP
|
||||
/* We have to check if a host already has configured our random
|
||||
* created link local address and continously check if there is
|
||||
* created link local address and continuously check if there is
|
||||
* a host with this IP-address so we can detect collisions */
|
||||
autoip_arp_reply(netif, hdr);
|
||||
#endif /* LWIP_AUTOIP */
|
||||
|
||||
/* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
|
||||
/* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
|
||||
* structure packing (not using structure copy which breaks strict-aliasing rules). */
|
||||
IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
|
||||
IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
|
||||
|
||||
/* this interface is not configured? */
|
||||
if (ip_addr_isany(&netif->ip_addr)) {
|
||||
if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
|
||||
for_us = 0;
|
||||
} else {
|
||||
/* ARP packet directed to us? */
|
||||
for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
|
||||
for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
|
||||
}
|
||||
|
||||
/* ARP message directed to us?
|
||||
@@ -767,7 +806,7 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
|
||||
case PP_HTONS(ARP_REQUEST):
|
||||
/* ARP request. If it asked for our address, we send out a
|
||||
* reply. In any case, we time-stamp any existing ARP entry,
|
||||
* and possiby send out an IP packet that was queued on it. */
|
||||
* and possibly send out an IP packet that was queued on it. */
|
||||
|
||||
LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
|
||||
/* ARP request for our address? */
|
||||
@@ -780,15 +819,15 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
|
||||
hdr->opcode = htons(ARP_REPLY);
|
||||
|
||||
IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
|
||||
IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
|
||||
IPADDR2_COPY(&hdr->sipaddr, netif_ip4_addr(netif));
|
||||
|
||||
LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
|
||||
(netif->hwaddr_len == ETHARP_HWADDR_LEN));
|
||||
LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
|
||||
(netif->hwaddr_len == ETH_HWADDR_LEN));
|
||||
#if LWIP_AUTOIP
|
||||
/* If we are using Link-Local, all ARP packets that contain a Link-Local
|
||||
* 'sender IP address' MUST be sent using link-layer broadcast instead of
|
||||
* link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
|
||||
ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
|
||||
ethdst_hwaddr = ip4_addr_islinklocal(netif_ip4_addr(netif)) ? (const u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
|
||||
#endif /* LWIP_AUTOIP */
|
||||
|
||||
ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
|
||||
@@ -806,7 +845,7 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
|
||||
/* return ARP reply */
|
||||
netif->linkoutput(netif, p);
|
||||
/* we are not configured? */
|
||||
} else if (ip_addr_isany(&netif->ip_addr)) {
|
||||
} else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
|
||||
/* { for_us == 0 and netif->ip_addr.addr == 0 } */
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
|
||||
/* request was not directed to us */
|
||||
@@ -846,10 +885,17 @@ etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
|
||||
/* if arp table entry is about to expire: re-request it,
|
||||
but only if its state is ETHARP_STATE_STABLE to prevent flooding the
|
||||
network with ARP requests if this address is used frequently. */
|
||||
if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) &&
|
||||
(arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) {
|
||||
if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) {
|
||||
if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
|
||||
/* issue a standard request using broadcast */
|
||||
if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
|
||||
arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING;
|
||||
arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
|
||||
}
|
||||
} else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
|
||||
/* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */
|
||||
if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
|
||||
arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,18 +922,22 @@ etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
|
||||
* or the return type of either etharp_query() or etharp_send_ip().
|
||||
*/
|
||||
err_t
|
||||
etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
|
||||
etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
|
||||
{
|
||||
struct eth_addr *dest;
|
||||
const struct eth_addr *dest;
|
||||
struct eth_addr mcastaddr;
|
||||
ip_addr_t *dst_addr = ipaddr;
|
||||
const ip4_addr_t *dst_addr = ipaddr;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", netif != NULL);
|
||||
LWIP_ASSERT("q != NULL", q != NULL);
|
||||
LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
|
||||
|
||||
/* make room for Ethernet header - should not fail */
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
if (pbuf_header(q, sizeof(struct eth_hdr) + SIZEOF_VLAN_HDR) != 0) {
|
||||
#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
/* bail out */
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
|
||||
("etharp_output: could not allocate room for header.\n"));
|
||||
@@ -899,15 +949,15 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
|
||||
* are special, other IP addresses are looked up in the ARP table. */
|
||||
|
||||
/* broadcast destination IP address? */
|
||||
if (ip_addr_isbroadcast(ipaddr, netif)) {
|
||||
if (ip4_addr_isbroadcast(ipaddr, netif)) {
|
||||
/* broadcast on Ethernet also */
|
||||
dest = (struct eth_addr *)ðbroadcast;
|
||||
dest = (const struct eth_addr *)ðbroadcast;
|
||||
/* multicast destination IP address? */
|
||||
} else if (ip_addr_ismulticast(ipaddr)) {
|
||||
} else if (ip4_addr_ismulticast(ipaddr)) {
|
||||
/* Hash IP multicast address to MAC address.*/
|
||||
mcastaddr.addr[0] = LL_MULTICAST_ADDR_0;
|
||||
mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
|
||||
mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
|
||||
mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0;
|
||||
mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1;
|
||||
mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2;
|
||||
mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
|
||||
mcastaddr.addr[4] = ip4_addr3(ipaddr);
|
||||
mcastaddr.addr[5] = ip4_addr4(ipaddr);
|
||||
@@ -918,22 +968,32 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
|
||||
s8_t i;
|
||||
/* outside local network? if so, this can neither be a global broadcast nor
|
||||
a subnet broadcast. */
|
||||
if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
|
||||
!ip_addr_islinklocal(ipaddr)) {
|
||||
if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
|
||||
!ip4_addr_islinklocal(ipaddr)) {
|
||||
#if LWIP_AUTOIP
|
||||
struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
SIZEOF_VLAN_HDR +
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
sizeof(struct eth_hdr));
|
||||
/* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
|
||||
a link-local source address must always be "directly to its destination
|
||||
on the same physical link. The host MUST NOT send the packet to any
|
||||
router for forwarding". */
|
||||
if (!ip_addr_islinklocal(&iphdr->src))
|
||||
if (!ip4_addr_islinklocal(&iphdr->src))
|
||||
#endif /* LWIP_AUTOIP */
|
||||
{
|
||||
#ifdef LWIP_HOOK_ETHARP_GET_GW
|
||||
/* For advanced routing, a single default gateway might not be enough, so get
|
||||
the IP address of the gateway to handle the current destination address. */
|
||||
dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
|
||||
if (dst_addr == NULL)
|
||||
#endif /* LWIP_HOOK_ETHARP_GET_GW */
|
||||
{
|
||||
/* interface has default gateway? */
|
||||
if (!ip_addr_isany(&netif->gw)) {
|
||||
if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) {
|
||||
/* send to hardware address of default gateway IP address */
|
||||
dst_addr = &(netif->gw);
|
||||
dst_addr = netif_ip4_gw(netif);
|
||||
/* no default gateway available */
|
||||
} else {
|
||||
/* no route to destination error (default gateway missing) */
|
||||
@@ -941,6 +1001,7 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if LWIP_NETIF_HWADDRHINT
|
||||
if (netif->addr_hint != NULL) {
|
||||
/* per-pcb cached entry was given */
|
||||
@@ -948,7 +1009,7 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
|
||||
if (etharp_cached_entry < ARP_TABLE_SIZE) {
|
||||
#endif /* LWIP_NETIF_HWADDRHINT */
|
||||
if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
|
||||
(ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
|
||||
(ip4_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
|
||||
/* the per-pcb-cached entry is stable and the right one! */
|
||||
ETHARP_STATS_INC(etharp.cachehit);
|
||||
return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
|
||||
@@ -962,7 +1023,7 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
|
||||
throughput and etharp_find_entry() is kind of slow */
|
||||
for (i = 0; i < ARP_TABLE_SIZE; i++) {
|
||||
if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
|
||||
(ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
|
||||
(ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
|
||||
/* found an existing, stable entry */
|
||||
ETHARP_SET_HINT(netif, i);
|
||||
return etharp_output_to_arp_index(netif, q, i);
|
||||
@@ -1013,22 +1074,23 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
|
||||
*
|
||||
*/
|
||||
err_t
|
||||
etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
|
||||
etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
|
||||
{
|
||||
struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
|
||||
err_t result = ERR_MEM;
|
||||
int is_new_entry = 0;
|
||||
s8_t i; /* ARP entry index */
|
||||
|
||||
/* non-unicast address? */
|
||||
if (ip_addr_isbroadcast(ipaddr, netif) ||
|
||||
ip_addr_ismulticast(ipaddr) ||
|
||||
ip_addr_isany(ipaddr)) {
|
||||
if (ip4_addr_isbroadcast(ipaddr, netif) ||
|
||||
ip4_addr_ismulticast(ipaddr) ||
|
||||
ip4_addr_isany(ipaddr)) {
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
/* find entry in ARP cache, ask to create entry if queueing packet */
|
||||
i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
|
||||
i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
|
||||
|
||||
/* could not find or create entry? */
|
||||
if (i < 0) {
|
||||
@@ -1042,7 +1104,10 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
|
||||
|
||||
/* mark a fresh entry as pending (we just sent a request) */
|
||||
if (arp_table[i].state == ETHARP_STATE_EMPTY) {
|
||||
is_new_entry = 1;
|
||||
arp_table[i].state = ETHARP_STATE_PENDING;
|
||||
/* record network interface for re-sending arp request in etharp_tmr */
|
||||
arp_table[i].netif = netif;
|
||||
}
|
||||
|
||||
/* { i is either a STABLE or (new or existing) PENDING entry } */
|
||||
@@ -1050,8 +1115,8 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
|
||||
((arp_table[i].state == ETHARP_STATE_PENDING) ||
|
||||
(arp_table[i].state >= ETHARP_STATE_STABLE)));
|
||||
|
||||
/* do we have a pending entry? or an implicit query request? */
|
||||
if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
|
||||
/* do we have a new entry? or an implicit query request? */
|
||||
if (is_new_entry || (q == NULL)) {
|
||||
/* try to resolve it; send out ARP request */
|
||||
result = etharp_request(netif, ipaddr);
|
||||
if (result != ERR_OK) {
|
||||
@@ -1092,7 +1157,7 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
|
||||
}
|
||||
if (copy_needed) {
|
||||
/* copy the whole packet into new pbufs */
|
||||
p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
|
||||
p = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM);
|
||||
if (p != NULL) {
|
||||
if (pbuf_copy(p, q) != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
@@ -1112,20 +1177,32 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
|
||||
/* allocate a new arp queue entry */
|
||||
new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
|
||||
if (new_entry != NULL) {
|
||||
unsigned int qlen = 0;
|
||||
new_entry->next = 0;
|
||||
new_entry->p = p;
|
||||
if (arp_table[i].q != NULL) {
|
||||
/* queue was already existent, append the new entry to the end */
|
||||
struct etharp_q_entry *r;
|
||||
r = arp_table[i].q;
|
||||
qlen++;
|
||||
while (r->next != NULL) {
|
||||
r = r->next;
|
||||
qlen++;
|
||||
}
|
||||
r->next = new_entry;
|
||||
} else {
|
||||
/* queue did not exist, first item in queue */
|
||||
arp_table[i].q = new_entry;
|
||||
}
|
||||
#if ARP_QUEUE_LEN
|
||||
if (qlen >= ARP_QUEUE_LEN) {
|
||||
struct etharp_q_entry *old;
|
||||
old = arp_table[i].q;
|
||||
arp_table[i].q = arp_table[i].q->next;
|
||||
pbuf_free(old->p);
|
||||
memp_free(MEMP_ARP_QUEUE, old);
|
||||
}
|
||||
#endif
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
|
||||
result = ERR_OK;
|
||||
} else {
|
||||
@@ -1174,13 +1251,16 @@ static
|
||||
err_t
|
||||
etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
|
||||
const struct eth_addr *ethdst_addr,
|
||||
const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
|
||||
const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
|
||||
const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
|
||||
const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
|
||||
const u16_t opcode)
|
||||
{
|
||||
struct pbuf *p;
|
||||
err_t result = ERR_OK;
|
||||
struct eth_hdr *ethhdr;
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
struct eth_vlan_hdr *vlanhdr;
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
struct etharp_hdr *hdr;
|
||||
#if LWIP_AUTOIP
|
||||
const u8_t * ethdst_hwaddr;
|
||||
@@ -1189,7 +1269,7 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
|
||||
LWIP_ASSERT("netif != NULL", netif != NULL);
|
||||
|
||||
/* allocate a pbuf for the outgoing ARP request packet */
|
||||
p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
|
||||
p = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETHARP_PACKET_TX, PBUF_RAM);
|
||||
/* could allocate a pbuf for an ARP request? */
|
||||
if (p == NULL) {
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
|
||||
@@ -1198,32 +1278,30 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
|
||||
return ERR_MEM;
|
||||
}
|
||||
LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
|
||||
(p->len >= SIZEOF_ETHARP_PACKET));
|
||||
(p->len >= SIZEOF_ETHARP_PACKET_TX));
|
||||
|
||||
ethhdr = (struct eth_hdr *)p->payload;
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR);
|
||||
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
|
||||
#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
|
||||
hdr->opcode = htons(opcode);
|
||||
|
||||
LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
|
||||
(netif->hwaddr_len == ETHARP_HWADDR_LEN));
|
||||
LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
|
||||
(netif->hwaddr_len == ETH_HWADDR_LEN));
|
||||
#if LWIP_AUTOIP
|
||||
/* If we are using Link-Local, all ARP packets that contain a Link-Local
|
||||
* 'sender IP address' MUST be sent using link-layer broadcast instead of
|
||||
* link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
|
||||
ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
|
||||
ethdst_hwaddr = ip4_addr_islinklocal(ipsrc_addr) ? (const u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
|
||||
#endif /* LWIP_AUTOIP */
|
||||
/* Write the ARP MAC-Addresses */
|
||||
ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
|
||||
ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
|
||||
/* Write the Ethernet MAC-Addresses */
|
||||
#if LWIP_AUTOIP
|
||||
ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr);
|
||||
#else /* LWIP_AUTOIP */
|
||||
ETHADDR16_COPY(ðhdr->dest, ethdst_addr);
|
||||
#endif /* LWIP_AUTOIP */
|
||||
ETHADDR16_COPY(ðhdr->src, ethsrc_addr);
|
||||
/* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
|
||||
/* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
|
||||
* structure packing. */
|
||||
IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
|
||||
IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
|
||||
@@ -1231,10 +1309,31 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
|
||||
hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
|
||||
hdr->proto = PP_HTONS(ETHTYPE_IP);
|
||||
/* set hwlen and protolen */
|
||||
hdr->hwlen = ETHARP_HWADDR_LEN;
|
||||
hdr->protolen = sizeof(ip_addr_t);
|
||||
hdr->hwlen = ETH_HWADDR_LEN;
|
||||
hdr->protolen = sizeof(ip4_addr_t);
|
||||
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
ethhdr->type = PP_HTONS(ETHTYPE_VLAN);
|
||||
vlanhdr->tpid = PP_HTONS(ETHTYPE_ARP);
|
||||
vlanhdr->prio_vid = 0;
|
||||
if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) {
|
||||
/* packet shall not contain VLAN header, so hide it and set correct ethertype */
|
||||
pbuf_header(p, -SIZEOF_VLAN_HDR);
|
||||
ethhdr = (struct eth_hdr *)p->payload;
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
ethhdr->type = PP_HTONS(ETHTYPE_ARP);
|
||||
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
|
||||
}
|
||||
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
|
||||
|
||||
/* Write the Ethernet MAC-Addresses */
|
||||
#if LWIP_AUTOIP
|
||||
ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr);
|
||||
#else /* LWIP_AUTOIP */
|
||||
ETHADDR16_COPY(ðhdr->dest, ethdst_addr);
|
||||
#endif /* LWIP_AUTOIP */
|
||||
ETHADDR16_COPY(ðhdr->src, ethsrc_addr);
|
||||
|
||||
/* send ARP query */
|
||||
result = netif->linkoutput(netif, p);
|
||||
ETHARP_STATS_INC(etharp.xmit);
|
||||
@@ -1246,6 +1345,26 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ARP request packet asking for ipaddr to a specific eth address.
|
||||
* Used to send unicast request to refresh the ARP table just before an entry
|
||||
* times out
|
||||
*
|
||||
* @param netif the lwip network interface on which to send the request
|
||||
* @param ipaddr the IP address for which to ask
|
||||
* @param hw_dst_addr the ethernet address to send this packet to
|
||||
* @return ERR_OK if the request has been sent
|
||||
* ERR_MEM if the ARP packet couldn't be allocated
|
||||
* any other err_t on failure
|
||||
*/
|
||||
static err_t
|
||||
etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr)
|
||||
{
|
||||
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
|
||||
(struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), ðzero,
|
||||
ipaddr, ARP_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ARP request packet asking for ipaddr.
|
||||
*
|
||||
@@ -1256,144 +1375,11 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
|
||||
* any other err_t on failure
|
||||
*/
|
||||
err_t
|
||||
etharp_request(struct netif *netif, ip_addr_t *ipaddr)
|
||||
etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
|
||||
{
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
|
||||
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
|
||||
(struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero,
|
||||
ipaddr, ARP_REQUEST);
|
||||
return etharp_request_dst(netif, ipaddr, ðbroadcast);
|
||||
}
|
||||
#endif /* LWIP_ARP */
|
||||
#endif /* LWIP_IPV4 && LWIP_ARP */
|
||||
|
||||
/**
|
||||
* Process received ethernet frames. Using this function instead of directly
|
||||
* calling ip_input and passing ARP frames through etharp in ethernetif_input,
|
||||
* the ARP cache is protected from concurrent access.
|
||||
*
|
||||
* @param p the recevied packet, p->payload pointing to the ethernet header
|
||||
* @param netif the network interface on which the packet was received
|
||||
*/
|
||||
err_t
|
||||
ethernet_input(struct pbuf *p, struct netif *netif)
|
||||
{
|
||||
struct eth_hdr* ethhdr;
|
||||
u16_t type;
|
||||
#if LWIP_ARP || ETHARP_SUPPORT_VLAN
|
||||
s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
|
||||
#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
|
||||
|
||||
if (p->len <= SIZEOF_ETH_HDR) {
|
||||
/* a packet with only an ethernet header (or less) is not valid for us */
|
||||
ETHARP_STATS_INC(etharp.proterr);
|
||||
ETHARP_STATS_INC(etharp.drop);
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
/* points to packet payload, which starts with an Ethernet header */
|
||||
ethhdr = (struct eth_hdr *)p->payload;
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
|
||||
("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
|
||||
(unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
|
||||
(unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
|
||||
(unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
|
||||
(unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
|
||||
(unsigned)htons(ethhdr->type)));
|
||||
|
||||
type = ethhdr->type;
|
||||
#if ETHARP_SUPPORT_VLAN
|
||||
if (type == PP_HTONS(ETHTYPE_VLAN)) {
|
||||
struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
|
||||
if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
|
||||
/* a packet with only an ethernet/vlan header (or less) is not valid for us */
|
||||
ETHARP_STATS_INC(etharp.proterr);
|
||||
ETHARP_STATS_INC(etharp.drop);
|
||||
goto free_and_return;
|
||||
}
|
||||
#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
|
||||
#ifdef ETHARP_VLAN_CHECK_FN
|
||||
if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
|
||||
#elif defined(ETHARP_VLAN_CHECK)
|
||||
if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
|
||||
#endif
|
||||
/* silently ignore this packet: not for our VLAN */
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
|
||||
type = vlan->tpid;
|
||||
ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
|
||||
}
|
||||
#endif /* ETHARP_SUPPORT_VLAN */
|
||||
|
||||
#if LWIP_ARP_FILTER_NETIF
|
||||
netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type));
|
||||
#endif /* LWIP_ARP_FILTER_NETIF*/
|
||||
|
||||
if (ethhdr->dest.addr[0] & 1) {
|
||||
/* this might be a multicast or broadcast packet */
|
||||
if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) {
|
||||
if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) &&
|
||||
(ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) {
|
||||
/* mark the pbuf as link-layer multicast */
|
||||
p->flags |= PBUF_FLAG_LLMCAST;
|
||||
}
|
||||
} else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) {
|
||||
/* mark the pbuf as link-layer broadcast */
|
||||
p->flags |= PBUF_FLAG_LLBCAST;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
#if LWIP_ARP
|
||||
/* IP packet? */
|
||||
case PP_HTONS(ETHTYPE_IP):
|
||||
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
|
||||
goto free_and_return;
|
||||
}
|
||||
#if ETHARP_TRUST_IP_MAC
|
||||
/* update ARP table */
|
||||
etharp_ip_input(netif, p);
|
||||
#endif /* ETHARP_TRUST_IP_MAC */
|
||||
/* skip Ethernet header */
|
||||
if(pbuf_header(p, -ip_hdr_offset)) {
|
||||
LWIP_ASSERT("Can't move over header in packet", 0);
|
||||
goto free_and_return;
|
||||
} else {
|
||||
/* pass to IP layer */
|
||||
ip_input(p, netif);
|
||||
}
|
||||
break;
|
||||
|
||||
case PP_HTONS(ETHTYPE_ARP):
|
||||
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
|
||||
goto free_and_return;
|
||||
}
|
||||
/* pass p to ARP module */
|
||||
etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
|
||||
break;
|
||||
#endif /* LWIP_ARP */
|
||||
#if PPPOE_SUPPORT
|
||||
case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
|
||||
pppoe_disc_input(netif, p);
|
||||
break;
|
||||
|
||||
case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
|
||||
pppoe_data_input(netif, p);
|
||||
break;
|
||||
#endif /* PPPOE_SUPPORT */
|
||||
|
||||
default:
|
||||
ETHARP_STATS_INC(etharp.proterr);
|
||||
ETHARP_STATS_INC(etharp.drop);
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
/* This means the pbuf is freed or consumed,
|
||||
so the caller doesn't have to free it again */
|
||||
return ERR_OK;
|
||||
|
||||
free_and_return:
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_ARP || LWIP_ETHERNET */
|
||||
@@ -41,14 +41,13 @@
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
|
||||
#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/snmp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -70,7 +69,7 @@ static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
|
||||
* Currently only processes icmp echo requests and sends
|
||||
* out the echo response.
|
||||
*
|
||||
* @param p the icmp echo request packet, p->payload pointing to the ip header
|
||||
* @param p the icmp echo request packet, p->payload pointing to the icmp header
|
||||
* @param inp the netif on which this packet was received
|
||||
*/
|
||||
void
|
||||
@@ -81,16 +80,20 @@ icmp_input(struct pbuf *p, struct netif *inp)
|
||||
u8_t code;
|
||||
#endif /* LWIP_DEBUG */
|
||||
struct icmp_echo_hdr *iecho;
|
||||
struct ip_hdr *iphdr;
|
||||
const struct ip_hdr *iphdr_in;
|
||||
s16_t hlen;
|
||||
const ip4_addr_t* src;
|
||||
|
||||
ICMP_STATS_INC(icmp.recv);
|
||||
snmp_inc_icmpinmsgs();
|
||||
MIB2_STATS_INC(mib2.icmpinmsgs);
|
||||
|
||||
|
||||
iphdr = (struct ip_hdr *)p->payload;
|
||||
hlen = IPH_HL(iphdr) * 4;
|
||||
if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {
|
||||
iphdr_in = ip4_current_header();
|
||||
hlen = IPH_HL(iphdr_in) * 4;
|
||||
if (hlen < IP_HLEN) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
|
||||
goto lenerr;
|
||||
}
|
||||
if (p->len < sizeof(u16_t)*2) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
|
||||
goto lenerr;
|
||||
}
|
||||
@@ -103,83 +106,87 @@ icmp_input(struct pbuf *p, struct netif *inp)
|
||||
case ICMP_ER:
|
||||
/* This is OK, echo reply might have been parsed by a raw PCB
|
||||
(as obviously, an echo request has been sent, too). */
|
||||
MIB2_STATS_INC(mib2.icmpinechoreps);
|
||||
break;
|
||||
case ICMP_ECHO:
|
||||
#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
|
||||
{
|
||||
int accepted = 1;
|
||||
#if !LWIP_MULTICAST_PING
|
||||
MIB2_STATS_INC(mib2.icmpinechos);
|
||||
src = ip4_current_dest_addr();
|
||||
/* multicast destination address? */
|
||||
if (ip_addr_ismulticast(¤t_iphdr_dest)) {
|
||||
accepted = 0;
|
||||
}
|
||||
if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
|
||||
#if LWIP_MULTICAST_PING
|
||||
/* For multicast, use address of receiving interface as source address */
|
||||
src = netif_ip4_addr(inp);
|
||||
#else /* LWIP_MULTICAST_PING */
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n"));
|
||||
goto icmperr;
|
||||
#endif /* LWIP_MULTICAST_PING */
|
||||
#if !LWIP_BROADCAST_PING
|
||||
}
|
||||
/* broadcast destination address? */
|
||||
if (ip_addr_isbroadcast(¤t_iphdr_dest, inp)) {
|
||||
accepted = 0;
|
||||
}
|
||||
if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
|
||||
#if LWIP_BROADCAST_PING
|
||||
/* For broadcast, use address of receiving interface as source address */
|
||||
src = netif_ip4_addr(inp);
|
||||
#else /* LWIP_BROADCAST_PING */
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));
|
||||
goto icmperr;
|
||||
#endif /* LWIP_BROADCAST_PING */
|
||||
/* broadcast or multicast destination address not acceptd? */
|
||||
if (!accepted) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
|
||||
ICMP_STATS_INC(icmp.err);
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
|
||||
if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
|
||||
goto lenerr;
|
||||
}
|
||||
#if CHECKSUM_CHECK_ICMP
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) {
|
||||
if (inet_chksum_pbuf(p) != 0) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
|
||||
pbuf_free(p);
|
||||
ICMP_STATS_INC(icmp.chkerr);
|
||||
snmp_inc_icmpinerrors();
|
||||
MIB2_STATS_INC(mib2.icmpinerrors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
|
||||
if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
|
||||
if (pbuf_header(p, (hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
|
||||
/* p is not big enough to contain link headers
|
||||
* allocate a new one and copy p into it
|
||||
*/
|
||||
struct pbuf *r;
|
||||
/* switch p->payload to ip header */
|
||||
if (pbuf_header(p, hlen)) {
|
||||
LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
|
||||
goto memerr;
|
||||
}
|
||||
/* allocate new packet buffer with space for link headers */
|
||||
r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
|
||||
r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, PBUF_RAM);
|
||||
if (r == NULL) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
|
||||
goto memerr;
|
||||
goto icmperr;
|
||||
}
|
||||
LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
|
||||
(r->len >= hlen + sizeof(struct icmp_echo_hdr)));
|
||||
/* copy the whole packet including ip header */
|
||||
if (pbuf_copy(r, p) != ERR_OK) {
|
||||
LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
|
||||
goto memerr;
|
||||
if (r->len < hlen + sizeof(struct icmp_echo_hdr)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header"));
|
||||
pbuf_free(r);
|
||||
goto icmperr;
|
||||
}
|
||||
iphdr = (struct ip_hdr *)r->payload;
|
||||
/* switch r->payload back to icmp header */
|
||||
/* copy the ip header */
|
||||
MEMCPY(r->payload, iphdr_in, hlen);
|
||||
/* switch r->payload back to icmp header (cannot fail) */
|
||||
if (pbuf_header(r, -hlen)) {
|
||||
LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
|
||||
goto memerr;
|
||||
LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);
|
||||
pbuf_free(r);
|
||||
goto icmperr;
|
||||
}
|
||||
/* copy the rest of the packet without ip header */
|
||||
if (pbuf_copy(r, p) != ERR_OK) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed"));
|
||||
pbuf_free(r);
|
||||
goto icmperr;
|
||||
}
|
||||
/* free the original p */
|
||||
pbuf_free(p);
|
||||
/* we now have an identical copy of p that has room for link headers */
|
||||
p = r;
|
||||
} else {
|
||||
/* restore p->payload to point to icmp header */
|
||||
if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
|
||||
/* restore p->payload to point to icmp header (cannot fail) */
|
||||
if (pbuf_header(p, -(s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
|
||||
LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
|
||||
goto memerr;
|
||||
goto icmperr;
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
|
||||
@@ -187,16 +194,28 @@ icmp_input(struct pbuf *p, struct netif *inp)
|
||||
/* We generate an answer by switching the dest and src ip addresses,
|
||||
* setting the icmp type to ECHO_RESPONSE and updating the checksum. */
|
||||
iecho = (struct icmp_echo_hdr *)p->payload;
|
||||
ip_addr_copy(iphdr->src, *ip_current_dest_addr());
|
||||
ip_addr_copy(iphdr->dest, *ip_current_src_addr());
|
||||
if (pbuf_header(p, hlen)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
|
||||
} else {
|
||||
err_t ret;
|
||||
struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
|
||||
ip4_addr_copy(iphdr->src, *src);
|
||||
ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
|
||||
ICMPH_TYPE_SET(iecho, ICMP_ER);
|
||||
#if CHECKSUM_GEN_ICMP
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
|
||||
/* adjust the checksum */
|
||||
if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
|
||||
if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
|
||||
iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
|
||||
} else {
|
||||
iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
|
||||
}
|
||||
}
|
||||
#if LWIP_CHECKSUM_CTRL_PER_NETIF
|
||||
else {
|
||||
iecho->chksum = 0;
|
||||
}
|
||||
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
|
||||
#else /* CHECKSUM_GEN_ICMP */
|
||||
iecho->chksum = 0;
|
||||
#endif /* CHECKSUM_GEN_ICMP */
|
||||
@@ -205,28 +224,45 @@ icmp_input(struct pbuf *p, struct netif *inp)
|
||||
IPH_TTL_SET(iphdr, ICMP_TTL);
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
#if CHECKSUM_GEN_IP
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_IP */
|
||||
|
||||
ICMP_STATS_INC(icmp.xmit);
|
||||
/* increase number of messages attempted to send */
|
||||
snmp_inc_icmpoutmsgs();
|
||||
MIB2_STATS_INC(mib2.icmpoutmsgs);
|
||||
/* increase number of echo replies attempted to send */
|
||||
snmp_inc_icmpoutechoreps();
|
||||
MIB2_STATS_INC(mib2.icmpoutechoreps);
|
||||
|
||||
if(pbuf_header(p, hlen)) {
|
||||
LWIP_ASSERT("Can't move over header in packet", 0);
|
||||
} else {
|
||||
err_t ret;
|
||||
/* send an ICMP packet, src addr is the dest addr of the curren packet */
|
||||
ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,
|
||||
/* send an ICMP packet */
|
||||
ret = ip4_output_if(p, src, IP_HDRINCL,
|
||||
ICMP_TTL, 0, IP_PROTO_ICMP, inp);
|
||||
if (ret != ERR_OK) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (type == ICMP_DUR) {
|
||||
MIB2_STATS_INC(mib2.icmpindestunreachs);
|
||||
} else if (type == ICMP_TE) {
|
||||
MIB2_STATS_INC(mib2.icmpindestunreachs);
|
||||
} else if (type == ICMP_PP) {
|
||||
MIB2_STATS_INC(mib2.icmpinparmprobs);
|
||||
} else if (type == ICMP_SQ) {
|
||||
MIB2_STATS_INC(mib2.icmpinsrcquenchs);
|
||||
} else if (type == ICMP_RD) {
|
||||
MIB2_STATS_INC(mib2.icmpinredirects);
|
||||
} else if (type == ICMP_TS) {
|
||||
MIB2_STATS_INC(mib2.icmpintimestamps);
|
||||
} else if (type == ICMP_TSR) {
|
||||
MIB2_STATS_INC(mib2.icmpintimestampreps);
|
||||
} else if (type == ICMP_AM) {
|
||||
MIB2_STATS_INC(mib2.icmpinaddrmasks);
|
||||
} else if (type == ICMP_AMR) {
|
||||
MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
|
||||
}
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
|
||||
(s16_t)type, (s16_t)code));
|
||||
ICMP_STATS_INC(icmp.proterr);
|
||||
@@ -237,15 +273,15 @@ icmp_input(struct pbuf *p, struct netif *inp)
|
||||
lenerr:
|
||||
pbuf_free(p);
|
||||
ICMP_STATS_INC(icmp.lenerr);
|
||||
snmp_inc_icmpinerrors();
|
||||
MIB2_STATS_INC(mib2.icmpinerrors);
|
||||
return;
|
||||
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
|
||||
memerr:
|
||||
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
|
||||
icmperr:
|
||||
pbuf_free(p);
|
||||
ICMP_STATS_INC(icmp.err);
|
||||
snmp_inc_icmpinerrors();
|
||||
MIB2_STATS_INC(mib2.icmpinerrors);
|
||||
return;
|
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
|
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,6 +296,7 @@ memerr:
|
||||
void
|
||||
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
|
||||
{
|
||||
MIB2_STATS_INC(mib2.icmpoutdestunreachs);
|
||||
icmp_send_response(p, ICMP_DUR, t);
|
||||
}
|
||||
|
||||
@@ -274,6 +311,7 @@ icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
|
||||
void
|
||||
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
|
||||
{
|
||||
MIB2_STATS_INC(mib2.icmpouttimeexcds);
|
||||
icmp_send_response(p, ICMP_TE, t);
|
||||
}
|
||||
|
||||
@@ -294,13 +332,18 @@ icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
|
||||
struct ip_hdr *iphdr;
|
||||
/* we can use the echo header here */
|
||||
struct icmp_echo_hdr *icmphdr;
|
||||
ip_addr_t iphdr_src;
|
||||
ip4_addr_t iphdr_src;
|
||||
struct netif *netif;
|
||||
|
||||
/* increase number of messages attempted to send */
|
||||
MIB2_STATS_INC(mib2.icmpoutmsgs);
|
||||
|
||||
/* ICMP header + IP header + 8 bytes of data */
|
||||
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
|
||||
PBUF_RAM);
|
||||
if (q == NULL) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
|
||||
MIB2_STATS_INC(mib2.icmpouterrors);
|
||||
return;
|
||||
}
|
||||
LWIP_ASSERT("check that first pbuf can hold icmp message",
|
||||
@@ -308,9 +351,9 @@ icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
|
||||
|
||||
iphdr = (struct ip_hdr *)p->payload;
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
|
||||
ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
|
||||
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
|
||||
LWIP_DEBUGF(ICMP_DEBUG, (" to "));
|
||||
ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
|
||||
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
|
||||
|
||||
icmphdr = (struct icmp_echo_hdr *)q->payload;
|
||||
@@ -323,17 +366,28 @@ icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
|
||||
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
|
||||
IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
|
||||
|
||||
ip4_addr_copy(iphdr_src, iphdr->src);
|
||||
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
|
||||
{
|
||||
ip4_addr_t iphdr_dst;
|
||||
ip4_addr_copy(iphdr_dst, iphdr->dest);
|
||||
netif = ip4_route_src(&iphdr_src, &iphdr_dst);
|
||||
}
|
||||
#else
|
||||
netif = ip4_route(&iphdr_src);
|
||||
#endif
|
||||
if (netif != NULL) {
|
||||
/* calculate checksum */
|
||||
icmphdr->chksum = 0;
|
||||
#if CHECKSUM_GEN_ICMP
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
|
||||
icmphdr->chksum = inet_chksum(icmphdr, q->len);
|
||||
}
|
||||
#endif
|
||||
ICMP_STATS_INC(icmp.xmit);
|
||||
/* increase number of messages attempted to send */
|
||||
snmp_inc_icmpoutmsgs();
|
||||
/* increase number of destination unreachable messages attempted to send */
|
||||
snmp_inc_icmpouttimeexcds();
|
||||
ip_addr_copy(iphdr_src, iphdr->src);
|
||||
ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);
|
||||
ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
|
||||
}
|
||||
pbuf_free(q);
|
||||
}
|
||||
|
||||
#endif /* LWIP_ICMP */
|
||||
#endif /* LWIP_IPV4 && LWIP_ICMP */
|
||||
@@ -38,6 +38,12 @@
|
||||
* source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup igmp IGMP
|
||||
* @ingroup ip4
|
||||
* To be called from TCPIP thread
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------
|
||||
Note 1)
|
||||
Although the rfc requires V1 AND V2 capability
|
||||
@@ -79,7 +85,7 @@ Steve Reynolds
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
|
||||
#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/debug.h"
|
||||
@@ -88,9 +94,6 @@ Steve Reynolds
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include "string.h"
|
||||
@@ -124,10 +127,10 @@ Steve Reynolds
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct igmp_msg {
|
||||
PACK_STRUCT_FIELD(u8_t igmp_msgtype);
|
||||
PACK_STRUCT_FIELD(u8_t igmp_maxresp);
|
||||
PACK_STRUCT_FLD_8(u8_t igmp_msgtype);
|
||||
PACK_STRUCT_FLD_8(u8_t igmp_maxresp);
|
||||
PACK_STRUCT_FIELD(u16_t igmp_checksum);
|
||||
PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address);
|
||||
PACK_STRUCT_FLD_S(ip4_addr_p_t igmp_group_address);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
@@ -135,18 +138,18 @@ PACK_STRUCT_END
|
||||
#endif
|
||||
|
||||
|
||||
static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
|
||||
static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
|
||||
static err_t igmp_remove_group(struct igmp_group *group);
|
||||
static void igmp_timeout( struct igmp_group *group);
|
||||
static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
|
||||
static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
|
||||
static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
|
||||
static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
|
||||
static void igmp_send(struct igmp_group *group, u8_t type);
|
||||
|
||||
|
||||
static struct igmp_group* igmp_group_list;
|
||||
static ip_addr_t allsystems;
|
||||
static ip_addr_t allrouters;
|
||||
static ip4_addr_t allsystems;
|
||||
static ip4_addr_t allrouters;
|
||||
|
||||
|
||||
/**
|
||||
@@ -161,27 +164,6 @@ igmp_init(void)
|
||||
IP4_ADDR(&allrouters, 224, 0, 0, 2);
|
||||
}
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
/**
|
||||
* Dump global IGMP groups list
|
||||
*/
|
||||
void
|
||||
igmp_dump_group_list()
|
||||
{
|
||||
struct igmp_group *group = igmp_group_list;
|
||||
|
||||
while (group != NULL) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
|
||||
ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
|
||||
group = group->next;
|
||||
}
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
|
||||
}
|
||||
#else
|
||||
#define igmp_dump_group_list()
|
||||
#endif /* LWIP_DEBUG */
|
||||
|
||||
/**
|
||||
* Start IGMP processing on interface
|
||||
*
|
||||
@@ -192,7 +174,7 @@ igmp_start(struct netif *netif)
|
||||
{
|
||||
struct igmp_group* group;
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void*)netif));
|
||||
|
||||
group = igmp_lookup_group(netif, &allsystems);
|
||||
|
||||
@@ -203,8 +185,8 @@ igmp_start(struct netif *netif)
|
||||
/* Allow the igmp messages at the MAC level */
|
||||
if (netif->igmp_mac_filter != NULL) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, &allsystems);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
|
||||
ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
|
||||
}
|
||||
|
||||
@@ -242,8 +224,8 @@ igmp_stop(struct netif *netif)
|
||||
/* disable the group at the MAC level */
|
||||
if (netif->igmp_mac_filter != NULL) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &group->group_address);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
|
||||
}
|
||||
/* free group */
|
||||
@@ -268,10 +250,10 @@ igmp_report_groups(struct netif *netif)
|
||||
{
|
||||
struct igmp_group *group = igmp_group_list;
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void*)netif));
|
||||
|
||||
while (group != NULL) {
|
||||
if (group->netif == netif) {
|
||||
if ((group->netif == netif) && (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
|
||||
igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
|
||||
}
|
||||
group = group->next;
|
||||
@@ -287,12 +269,12 @@ igmp_report_groups(struct netif *netif)
|
||||
* NULL if the group wasn't found.
|
||||
*/
|
||||
struct igmp_group *
|
||||
igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
|
||||
igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
|
||||
{
|
||||
struct igmp_group *group = igmp_group_list;
|
||||
|
||||
while (group != NULL) {
|
||||
if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
|
||||
if ((group->netif == ifp) && (ip4_addr_cmp(&(group->group_address), addr))) {
|
||||
return group;
|
||||
}
|
||||
group = group->next;
|
||||
@@ -313,9 +295,9 @@ igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
|
||||
* NULL on memory error.
|
||||
*/
|
||||
struct igmp_group *
|
||||
igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
|
||||
igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
|
||||
{
|
||||
struct igmp_group *group = igmp_group_list;
|
||||
struct igmp_group *group;
|
||||
|
||||
/* Search if the group already exists */
|
||||
group = igmp_lookfor_group(ifp, addr);
|
||||
@@ -328,7 +310,7 @@ igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
|
||||
group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
|
||||
if (group != NULL) {
|
||||
group->netif = ifp;
|
||||
ip_addr_set(&(group->group_address), addr);
|
||||
ip4_addr_set(&(group->group_address), addr);
|
||||
group->timer = 0; /* Not running */
|
||||
group->group_state = IGMP_GROUP_NON_MEMBER;
|
||||
group->last_reporter_flag = 0;
|
||||
@@ -339,8 +321,8 @@ igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
|
||||
ip_addr_debug_print(IGMP_DEBUG, addr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, addr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)ifp));
|
||||
|
||||
return group;
|
||||
}
|
||||
@@ -369,9 +351,10 @@ igmp_remove_group(struct igmp_group *group)
|
||||
}
|
||||
}
|
||||
/* Group not found in the global igmp_group_list */
|
||||
if (tmpGroup == NULL)
|
||||
if (tmpGroup == NULL) {
|
||||
err = ERR_ARG;
|
||||
}
|
||||
}
|
||||
/* free group */
|
||||
memp_free(MEMP_IGMP_GROUP, group);
|
||||
|
||||
@@ -381,14 +364,13 @@ igmp_remove_group(struct igmp_group *group)
|
||||
/**
|
||||
* Called from ip_input() if a new IGMP packet is received.
|
||||
*
|
||||
* @param p received igmp packet, p->payload pointing to the ip header
|
||||
* @param p received igmp packet, p->payload pointing to the igmp header
|
||||
* @param inp network interface on which the packet was received
|
||||
* @param dest destination ip address of the igmp packet
|
||||
*/
|
||||
void
|
||||
igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
|
||||
{
|
||||
struct ip_hdr * iphdr;
|
||||
struct igmp_msg* igmp;
|
||||
struct igmp_group* group;
|
||||
struct igmp_group* groupref;
|
||||
@@ -396,8 +378,7 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
IGMP_STATS_INC(igmp.recv);
|
||||
|
||||
/* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
|
||||
iphdr = (struct ip_hdr *)p->payload;
|
||||
if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
|
||||
if (p->len < IGMP_MINLEN) {
|
||||
pbuf_free(p);
|
||||
IGMP_STATS_INC(igmp.lenerr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
|
||||
@@ -405,10 +386,10 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->dest));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)inp));
|
||||
|
||||
/* Now calculate and check the checksum */
|
||||
igmp = (struct igmp_msg *)p->payload;
|
||||
@@ -432,9 +413,9 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
|
||||
/* NOW ACT ON THE INCOMING MESSAGE TYPE... */
|
||||
switch (igmp->igmp_msgtype) {
|
||||
case IGMP_MEMB_QUERY: {
|
||||
case IGMP_MEMB_QUERY:
|
||||
/* IGMP_MEMB_QUERY to the "all systems" address ? */
|
||||
if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
|
||||
if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
|
||||
/* THIS IS THE GENERAL QUERY */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
|
||||
|
||||
@@ -449,21 +430,21 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
groupref = igmp_group_list;
|
||||
while (groupref) {
|
||||
/* Do not send messages on the all systems group address! */
|
||||
if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
|
||||
if ((groupref->netif == inp) && (!(ip4_addr_cmp(&(groupref->group_address), &allsystems)))) {
|
||||
igmp_delaying_member(groupref, igmp->igmp_maxresp);
|
||||
}
|
||||
groupref = groupref->next;
|
||||
}
|
||||
} else {
|
||||
/* IGMP_MEMB_QUERY to a specific group ? */
|
||||
if (!ip_addr_isany(&igmp->igmp_group_address)) {
|
||||
if (!ip4_addr_isany(&igmp->igmp_group_address)) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
|
||||
if (ip_addr_cmp(dest, &allsystems)) {
|
||||
ip_addr_t groupaddr;
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
|
||||
if (ip4_addr_cmp(dest, &allsystems)) {
|
||||
ip4_addr_t groupaddr;
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
|
||||
/* we first need to re-look for the group since we used dest last time */
|
||||
ip_addr_copy(groupaddr, igmp->igmp_group_address);
|
||||
ip4_addr_copy(groupaddr, igmp->igmp_group_address);
|
||||
group = igmp_lookfor_group(inp, &groupaddr);
|
||||
} else {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
|
||||
@@ -480,8 +461,7 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IGMP_V2_MEMB_REPORT: {
|
||||
case IGMP_V2_MEMB_REPORT:
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
|
||||
IGMP_STATS_INC(igmp.rx_report);
|
||||
if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
|
||||
@@ -491,20 +471,19 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
group->last_reporter_flag = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
default:
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
|
||||
igmp->igmp_msgtype, group->group_state, &group, group->netif));
|
||||
igmp->igmp_msgtype, group->group_state, (void*)&group, (void*)group->netif));
|
||||
IGMP_STATS_INC(igmp.proterr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Join a group on one network interface.
|
||||
*
|
||||
* @param ifaddr ip address of the network interface which should join a new group
|
||||
@@ -512,39 +491,72 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
|
||||
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
|
||||
igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct igmp_group *group;
|
||||
struct netif *netif;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we join this interface ? */
|
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
|
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
|
||||
err = igmp_joingroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
/* Return an error even if some network interfaces are joined */
|
||||
/** @todo undo any other netif already joined */
|
||||
return err;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Join a group on one network interface.
|
||||
*
|
||||
* @param netif the network interface which should join a new group
|
||||
* @param groupaddr the ip address of the group which to join
|
||||
* @return ERR_OK if group was joined on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
struct igmp_group *group;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* make sure it is an igmp-enabled netif */
|
||||
LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
|
||||
|
||||
/* find group or create a new one if not found */
|
||||
group = igmp_lookup_group(netif, groupaddr);
|
||||
|
||||
if (group != NULL) {
|
||||
/* This should create a new group, check the state to make sure */
|
||||
if (group->group_state != IGMP_GROUP_NON_MEMBER) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
|
||||
} else {
|
||||
/* OK - it was new group */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
|
||||
|
||||
/* If first use of the group, allow the group at the MAC level */
|
||||
if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
|
||||
}
|
||||
|
||||
@@ -559,14 +571,42 @@ igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
|
||||
/* Increment group use */
|
||||
group->use++;
|
||||
/* Join on this interface */
|
||||
err = ERR_OK;
|
||||
return ERR_OK;
|
||||
} else {
|
||||
/* Return an error even if some network interfaces are joined */
|
||||
/** @todo undo any other netif already joined */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
|
||||
return ERR_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Leave a group on one network interface.
|
||||
*
|
||||
* @param ifaddr ip address of the network interface which should leave a group
|
||||
* @param groupaddr the ip address of the group which to leave
|
||||
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct netif *netif;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we leave this interface ? */
|
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
|
||||
err_t res = igmp_leavegroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
/* Store this result if we have not yet gotten a success */
|
||||
err = res;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
@@ -575,56 +615,53 @@ igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Leave a group on one network interface.
|
||||
*
|
||||
* @param ifaddr ip address of the network interface which should leave a group
|
||||
* @param netif the network interface which should leave a group
|
||||
* @param groupaddr the ip address of the group which to leave
|
||||
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
|
||||
* @return ERR_OK if group was left on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
|
||||
igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct igmp_group *group;
|
||||
struct netif *netif;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* make sure it is an igmp-enabled netif */
|
||||
LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we leave this interface ? */
|
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
|
||||
/* find group */
|
||||
group = igmp_lookfor_group(netif, groupaddr);
|
||||
|
||||
if (group != NULL) {
|
||||
/* Only send a leave if the flag is set according to the state diagram */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
|
||||
|
||||
/* If there is no other use of the group */
|
||||
if (group->use <= 1) {
|
||||
/* If we are the last reporter for this group */
|
||||
if (group->last_reporter_flag) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
|
||||
IGMP_STATS_INC(igmp.tx_leave);
|
||||
igmp_send(group, IGMP_LEAVE_GROUP);
|
||||
}
|
||||
|
||||
/* Disable the group at the MAC level */
|
||||
if (netif->igmp_mac_filter != NULL) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: remove group: "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
|
||||
|
||||
/* Free the group */
|
||||
@@ -633,19 +670,12 @@ igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
|
||||
/* Decrement group use */
|
||||
group->use--;
|
||||
}
|
||||
/* Leave on this interface */
|
||||
err = ERR_OK;
|
||||
return ERR_OK;
|
||||
} else {
|
||||
/* It's not a fatal error on "leavegroup" */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
|
||||
return ERR_VAL;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* The igmp timer function (both for NO_SYS=1 and =0)
|
||||
@@ -676,11 +706,13 @@ igmp_tmr(void)
|
||||
static void
|
||||
igmp_timeout(struct igmp_group *group)
|
||||
{
|
||||
/* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
|
||||
if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
|
||||
/* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
|
||||
(unless it is the allsystems group) */
|
||||
if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
|
||||
(!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
|
||||
ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)group->netif));
|
||||
|
||||
IGMP_STATS_INC(igmp.tx_report);
|
||||
igmp_send(group, IGMP_V2_MEMB_REPORT);
|
||||
@@ -697,12 +729,16 @@ igmp_timeout(struct igmp_group *group)
|
||||
static void
|
||||
igmp_start_timer(struct igmp_group *group, u8_t max_time)
|
||||
{
|
||||
/* ensure the input value is > 0 */
|
||||
if (max_time == 0) {
|
||||
max_time = 1;
|
||||
#ifdef LWIP_RAND
|
||||
group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1;
|
||||
#else /* LWIP_RAND */
|
||||
/* ATTENTION: use this only if absolutely necessary! */
|
||||
group->timer = max_time / 2;
|
||||
#endif /* LWIP_RAND */
|
||||
|
||||
if (group->timer == 0) {
|
||||
group->timer = 1;
|
||||
}
|
||||
/* ensure the random value is > 0 */
|
||||
group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -742,14 +778,14 @@ igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
|
||||
* returns errors returned by netif->output
|
||||
*/
|
||||
static err_t
|
||||
igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
|
||||
igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
|
||||
{
|
||||
/* This is the "router alert" option */
|
||||
u16_t ra[2];
|
||||
ra[0] = PP_HTONS(ROUTER_ALERT);
|
||||
ra[1] = 0x0000; /* Router shall examine packet */
|
||||
IGMP_STATS_INC(igmp.xmit);
|
||||
return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
|
||||
return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -763,8 +799,8 @@ igmp_send(struct igmp_group *group, u8_t type)
|
||||
{
|
||||
struct pbuf* p = NULL;
|
||||
struct igmp_msg* igmp = NULL;
|
||||
ip_addr_t src = *IP_ADDR_ANY;
|
||||
ip_addr_t* dest = NULL;
|
||||
ip4_addr_t src = *IP4_ADDR_ANY;
|
||||
ip4_addr_t* dest = NULL;
|
||||
|
||||
/* IP header + "router alert" option + IGMP header */
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
|
||||
@@ -773,16 +809,16 @@ igmp_send(struct igmp_group *group, u8_t type)
|
||||
igmp = (struct igmp_msg *)p->payload;
|
||||
LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
|
||||
(p->len >= sizeof(struct igmp_msg)));
|
||||
ip_addr_copy(src, group->netif->ip_addr);
|
||||
ip4_addr_copy(src, *netif_ip4_addr(group->netif));
|
||||
|
||||
if (type == IGMP_V2_MEMB_REPORT) {
|
||||
dest = &(group->group_address);
|
||||
ip_addr_copy(igmp->igmp_group_address, group->group_address);
|
||||
ip4_addr_copy(igmp->igmp_group_address, group->group_address);
|
||||
group->last_reporter_flag = 1; /* Remember we were the last to report */
|
||||
} else {
|
||||
if (type == IGMP_LEAVE_GROUP) {
|
||||
dest = &allrouters;
|
||||
ip_addr_copy(igmp->igmp_group_address, group->group_address);
|
||||
ip4_addr_copy(igmp->igmp_group_address, group->group_address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,4 +838,4 @@ igmp_send(struct igmp_group *group, u8_t type)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IGMP */
|
||||
#endif /* LWIP_IPV4 && LWIP_IGMP */
|
||||
@@ -39,30 +39,36 @@
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/ip_frag.h"
|
||||
#include "lwip/ip4_frag.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp_impl.h"
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/dhcp.h"
|
||||
#include "lwip/autoip.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "arch/perf.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/** Set this to 0 in the rare case of wanting to call an extra function to
|
||||
* generate the IP checksum (in contrast to calculating it on-the-fly). */
|
||||
#ifndef LWIP_INLINE_IP_CHKSUM
|
||||
#if LWIP_CHECKSUM_CTRL_PER_NETIF
|
||||
#define LWIP_INLINE_IP_CHKSUM 0
|
||||
#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */
|
||||
#define LWIP_INLINE_IP_CHKSUM 1
|
||||
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
|
||||
#endif
|
||||
|
||||
#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
|
||||
#define CHECKSUM_GEN_IP_INLINE 1
|
||||
#else
|
||||
@@ -74,7 +80,7 @@
|
||||
|
||||
/** Some defines for DHCP to let link-layer-addressed packets through while the
|
||||
* netif is down.
|
||||
* To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT
|
||||
* To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT(port)
|
||||
* to return 1 if the port is accepted and 0 if the port is not accepted.
|
||||
*/
|
||||
#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
|
||||
@@ -93,24 +99,40 @@
|
||||
#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
|
||||
#endif /* LWIP_DHCP */
|
||||
|
||||
/**
|
||||
* The interface that provided the packet for the current callback
|
||||
* invocation.
|
||||
*/
|
||||
struct netif *current_netif;
|
||||
|
||||
/**
|
||||
* Header of the input packet currently being processed.
|
||||
*/
|
||||
const struct ip_hdr *current_header;
|
||||
/** Source IP address of current_header */
|
||||
ip_addr_t current_iphdr_src;
|
||||
/** Destination IP address of current_header */
|
||||
ip_addr_t current_iphdr_dest;
|
||||
|
||||
/** The IP header ID of the next outgoing IP packet */
|
||||
static u16_t ip_id;
|
||||
|
||||
#if LWIP_MULTICAST_TX_OPTIONS
|
||||
/** The default netif used for multicast */
|
||||
static struct netif* ip4_default_multicast_netif;
|
||||
|
||||
/** Set a default netif for IPv4 multicast. */
|
||||
void
|
||||
ip4_set_default_multicast_netif(struct netif* default_multicast_netif)
|
||||
{
|
||||
ip4_default_multicast_netif = default_multicast_netif;
|
||||
}
|
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */
|
||||
|
||||
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
|
||||
/**
|
||||
* Source based IPv4 routing must be fully implemented in
|
||||
* LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides he parameters.
|
||||
*/
|
||||
struct netif *
|
||||
ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
|
||||
{
|
||||
if (src != NULL) {
|
||||
/* when src==NULL, the hook is called from ip4_route(dest) */
|
||||
struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, src);
|
||||
if (netif != NULL) {
|
||||
return netif;
|
||||
}
|
||||
}
|
||||
return ip4_route(dest);
|
||||
}
|
||||
#endif /* LWIP_HOOK_IP4_ROUTE_SRC */
|
||||
|
||||
/**
|
||||
* Finds the appropriate network interface for a given IP address. It
|
||||
* searches the list of network interfaces linearly. A match is found
|
||||
@@ -121,35 +143,74 @@ static u16_t ip_id;
|
||||
* @return the netif on which to send to reach dest
|
||||
*/
|
||||
struct netif *
|
||||
ip_route(ip_addr_t *dest)
|
||||
ip4_route(const ip4_addr_t *dest)
|
||||
{
|
||||
struct netif *netif;
|
||||
|
||||
#ifdef LWIP_HOOK_IP4_ROUTE
|
||||
#if LWIP_MULTICAST_TX_OPTIONS
|
||||
/* Use administratively selected interface for multicast by default */
|
||||
if (ip4_addr_ismulticast(dest) && ip4_default_multicast_netif) {
|
||||
return ip4_default_multicast_netif;
|
||||
}
|
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */
|
||||
|
||||
/* iterate through netifs */
|
||||
for (netif = netif_list; netif != NULL; netif = netif->next) {
|
||||
/* is the netif up, does it have a link and a valid address? */
|
||||
if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
|
||||
/* network mask matches? */
|
||||
if (ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {
|
||||
/* return netif on which to forward IP packet */
|
||||
return netif;
|
||||
}
|
||||
/* gateway matches on a non broadcast interface? (i.e. peer in a point to point interface) */
|
||||
if (((netif->flags & NETIF_FLAG_BROADCAST) == 0) && ip4_addr_cmp(dest, netif_ip4_gw(netif))) {
|
||||
/* return netif on which to forward IP packet */
|
||||
return netif;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
|
||||
/* loopif is disabled, looopback traffic is passed through any netif */
|
||||
if (ip4_addr_isloopback(dest)) {
|
||||
/* don't check for link on loopback traffic */
|
||||
if (netif_is_up(netif_default)) {
|
||||
return netif_default;
|
||||
}
|
||||
/* default netif is not up, just use any netif for loopback traffic */
|
||||
for (netif = netif_list; netif != NULL; netif = netif->next) {
|
||||
if (netif_is_up(netif)) {
|
||||
return netif;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
|
||||
|
||||
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
|
||||
netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, NULL);
|
||||
if (netif != NULL) {
|
||||
return netif;
|
||||
}
|
||||
#elif defined(LWIP_HOOK_IP4_ROUTE)
|
||||
netif = LWIP_HOOK_IP4_ROUTE(dest);
|
||||
if (netif != NULL) {
|
||||
return netif;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* iterate through netifs */
|
||||
for (netif = netif_list; netif != NULL; netif = netif->next) {
|
||||
/* network mask matches? */
|
||||
if (netif_is_up(netif)) {
|
||||
if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
|
||||
/* return netif on which to forward IP packet */
|
||||
return netif;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((netif_default == NULL) || (!netif_is_up(netif_default))) {
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) ||
|
||||
ip4_addr_isany_val(*netif_ip4_addr(netif_default))) {
|
||||
/* No matching netif found and default netif is not usable.
|
||||
If this is not good enough for you, use LWIP_HOOK_IP4_ROUTE() */
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
|
||||
IP_STATS_INC(ip.rterr);
|
||||
snmp_inc_ipoutnoroutes();
|
||||
MIB2_STATS_INC(mib2.ipoutnoroutes);
|
||||
return NULL;
|
||||
}
|
||||
/* no matching netif found, use default netif */
|
||||
|
||||
return netif_default;
|
||||
}
|
||||
|
||||
@@ -163,9 +224,9 @@ ip_route(ip_addr_t *dest)
|
||||
* @return 1: can forward 0: discard
|
||||
*/
|
||||
static int
|
||||
ip_canforward(struct pbuf *p)
|
||||
ip4_canforward(struct pbuf *p)
|
||||
{
|
||||
u32_t addr = ip4_addr_get_u32(ip_current_dest_addr());
|
||||
u32_t addr = htonl(ip4_addr_get_u32(ip4_current_dest_addr()));
|
||||
|
||||
if (p->flags & PBUF_FLAG_LLBCAST) {
|
||||
/* don't route link-layer broadcasts */
|
||||
@@ -181,7 +242,7 @@ ip_canforward(struct pbuf *p)
|
||||
}
|
||||
if (IP_CLASSA(addr)) {
|
||||
u32_t net = addr & IP_CLASSA_NET;
|
||||
if ((net == 0) || (net == (IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
|
||||
if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
|
||||
/* don't route loopback packets */
|
||||
return 0;
|
||||
}
|
||||
@@ -199,30 +260,30 @@ ip_canforward(struct pbuf *p)
|
||||
* @param inp the netif on which this packet was received
|
||||
*/
|
||||
static void
|
||||
ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
|
||||
ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
|
||||
{
|
||||
struct netif *netif;
|
||||
|
||||
PERF_START;
|
||||
|
||||
if (!ip_canforward(p)) {
|
||||
if (!ip4_canforward(p)) {
|
||||
goto return_noroute;
|
||||
}
|
||||
|
||||
/* RFC3927 2.7: do not forward link-local addresses */
|
||||
if (ip_addr_islinklocal(¤t_iphdr_dest)) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
|
||||
ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
|
||||
if (ip4_addr_islinklocal(ip4_current_dest_addr())) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
|
||||
ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
|
||||
goto return_noroute;
|
||||
}
|
||||
|
||||
/* Find network interface where to forward this IP packet to. */
|
||||
netif = ip_route(¤t_iphdr_dest);
|
||||
netif = ip4_route_src(ip4_current_dest_addr(), ip4_current_src_addr());
|
||||
if (netif == NULL) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
|
||||
ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
|
||||
ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
|
||||
ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
|
||||
ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
|
||||
/* @todo: send ICMP_DUR_NET? */
|
||||
goto return_noroute;
|
||||
}
|
||||
@@ -230,7 +291,7 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
|
||||
/* Do not forward packets onto the same network interface on which
|
||||
* they arrived. */
|
||||
if (netif == inp) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not bouncing packets back on incoming interface.\n"));
|
||||
goto return_noroute;
|
||||
}
|
||||
#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */
|
||||
@@ -239,7 +300,7 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
|
||||
IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
|
||||
/* send ICMP if TTL == 0 */
|
||||
if (IPH_TTL(iphdr) == 0) {
|
||||
snmp_inc_ipinhdrerrors();
|
||||
MIB2_STATS_INC(mib2.ipinhdrerrors);
|
||||
#if LWIP_ICMP
|
||||
/* Don't send ICMP messages in response to ICMP messages */
|
||||
if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
|
||||
@@ -256,34 +317,36 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
|
||||
IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
|
||||
ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
|
||||
ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
|
||||
|
||||
IP_STATS_INC(ip.fw);
|
||||
MIB2_STATS_INC(mib2.ipforwdatagrams);
|
||||
IP_STATS_INC(ip.xmit);
|
||||
snmp_inc_ipforwdatagrams();
|
||||
|
||||
PERF_STOP("ip_forward");
|
||||
PERF_STOP("ip4_forward");
|
||||
/* don't fragment if interface has mtu set to 0 [loopif] */
|
||||
if (netif->mtu && (p->tot_len > netif->mtu)) {
|
||||
if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) {
|
||||
#if IP_FRAG
|
||||
ip_frag(p, netif, ip_current_dest_addr());
|
||||
ip4_frag(p, netif, ip4_current_dest_addr());
|
||||
#else /* IP_FRAG */
|
||||
/* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */
|
||||
/* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */
|
||||
#endif /* IP_FRAG */
|
||||
} else {
|
||||
/* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */
|
||||
#if LWIP_ICMP
|
||||
/* send ICMP Destination Unreachable code 4: "Fragmentation Needed and DF Set" */
|
||||
icmp_dest_unreach(p, ICMP_DUR_FRAG);
|
||||
#endif /* LWIP_ICMP */
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* transmit pbuf on chosen interface */
|
||||
netif->output(netif, p, ¤t_iphdr_dest);
|
||||
netif->output(netif, p, ip4_current_dest_addr());
|
||||
return;
|
||||
return_noroute:
|
||||
snmp_inc_ipoutnoroutes();
|
||||
MIB2_STATS_INC(mib2.ipoutnoroutes);
|
||||
}
|
||||
#endif /* IP_FORWARD */
|
||||
|
||||
@@ -302,28 +365,28 @@ return_noroute:
|
||||
* processed, but currently always returns ERR_OK)
|
||||
*/
|
||||
err_t
|
||||
ip_input(struct pbuf *p, struct netif *inp)
|
||||
ip4_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
struct ip_hdr *iphdr;
|
||||
struct netif *netif;
|
||||
u16_t iphdr_hlen;
|
||||
u16_t iphdr_len;
|
||||
#if IP_ACCEPT_LINK_LAYER_ADDRESSING
|
||||
#if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP
|
||||
int check_ip_src = 1;
|
||||
#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
|
||||
#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */
|
||||
|
||||
IP_STATS_INC(ip.recv);
|
||||
snmp_inc_ipinreceives();
|
||||
MIB2_STATS_INC(mib2.ipinreceives);
|
||||
|
||||
/* identify the IP header */
|
||||
iphdr = (struct ip_hdr *)p->payload;
|
||||
if (IPH_V(iphdr) != 4) {
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
|
||||
ip_debug_print(p);
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", (u16_t)IPH_V(iphdr)));
|
||||
ip4_debug_print(p);
|
||||
pbuf_free(p);
|
||||
IP_STATS_INC(ip.err);
|
||||
IP_STATS_INC(ip.drop);
|
||||
snmp_inc_ipinhdrerrors();
|
||||
MIB2_STATS_INC(mib2.ipinhdrerrors);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -341,8 +404,17 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
/* obtain ip length in bytes */
|
||||
iphdr_len = ntohs(IPH_LEN(iphdr));
|
||||
|
||||
/* Trim pbuf. This is especially required for packets < 60 bytes. */
|
||||
if (iphdr_len < p->tot_len) {
|
||||
pbuf_realloc(p, iphdr_len);
|
||||
}
|
||||
|
||||
/* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
|
||||
if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
|
||||
if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) {
|
||||
if (iphdr_hlen < IP_HLEN) {
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
|
||||
("ip4_input: short IP header (%"U16_F" bytes) received, IP packet dropped\n", iphdr_hlen));
|
||||
}
|
||||
if (iphdr_hlen > p->len) {
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
|
||||
("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
|
||||
@@ -357,44 +429,54 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
pbuf_free(p);
|
||||
IP_STATS_INC(ip.lenerr);
|
||||
IP_STATS_INC(ip.drop);
|
||||
snmp_inc_ipindiscards();
|
||||
MIB2_STATS_INC(mib2.ipindiscards);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* verify checksum */
|
||||
#if CHECKSUM_CHECK_IP
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
|
||||
if (inet_chksum(iphdr, iphdr_hlen) != 0) {
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
|
||||
("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
|
||||
ip_debug_print(p);
|
||||
ip4_debug_print(p);
|
||||
pbuf_free(p);
|
||||
IP_STATS_INC(ip.chkerr);
|
||||
IP_STATS_INC(ip.drop);
|
||||
snmp_inc_ipinhdrerrors();
|
||||
MIB2_STATS_INC(mib2.ipinhdrerrors);
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Trim pbuf. This should have been done at the netif layer,
|
||||
* but we'll do it anyway just to be sure that its done. */
|
||||
pbuf_realloc(p, iphdr_len);
|
||||
|
||||
/* copy IP addresses to aligned ip_addr_t */
|
||||
ip_addr_copy(current_iphdr_dest, iphdr->dest);
|
||||
ip_addr_copy(current_iphdr_src, iphdr->src);
|
||||
ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest);
|
||||
ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src);
|
||||
|
||||
/* match packet against an interface, i.e. is this packet for us? */
|
||||
if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
|
||||
#if LWIP_IGMP
|
||||
if (ip_addr_ismulticast(¤t_iphdr_dest)) {
|
||||
if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ¤t_iphdr_dest))) {
|
||||
if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) {
|
||||
/* IGMP snooping switches need 0.0.0.0 to be allowed as source address (RFC 4541) */
|
||||
ip4_addr_t allsystems;
|
||||
IP4_ADDR(&allsystems, 224, 0, 0, 1);
|
||||
if (ip4_addr_cmp(ip4_current_dest_addr(), &allsystems) &&
|
||||
ip4_addr_isany(ip4_current_src_addr())) {
|
||||
check_ip_src = 0;
|
||||
}
|
||||
netif = inp;
|
||||
} else {
|
||||
netif = NULL;
|
||||
}
|
||||
#else /* LWIP_IGMP */
|
||||
if ((netif_is_up(inp)) && (!ip4_addr_isany_val(*netif_ip4_addr(inp)))) {
|
||||
netif = inp;
|
||||
} else {
|
||||
netif = NULL;
|
||||
}
|
||||
} else
|
||||
#endif /* LWIP_IGMP */
|
||||
{
|
||||
} else {
|
||||
/* start trying with inp. if that's not acceptable, start walking the
|
||||
list of configured netifs.
|
||||
'first' is used as a boolean to mark whether we started walking the list */
|
||||
@@ -402,18 +484,22 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
netif = inp;
|
||||
do {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
|
||||
ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr),
|
||||
ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask),
|
||||
ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask),
|
||||
ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask)));
|
||||
ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(netif_ip4_addr(netif)),
|
||||
ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
|
||||
ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
|
||||
ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(netif_ip4_netmask(netif))));
|
||||
|
||||
/* interface is up and configured? */
|
||||
if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
|
||||
if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) {
|
||||
/* unicast to this interface address? */
|
||||
if (ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) ||
|
||||
if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
|
||||
/* or broadcast on this interface network address? */
|
||||
ip_addr_isbroadcast(¤t_iphdr_dest, netif)) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
|
||||
ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
|
||||
#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
|
||||
|| (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK))
|
||||
#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
|
||||
) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n",
|
||||
netif->name[0], netif->name[1]));
|
||||
/* break out of for loop */
|
||||
break;
|
||||
@@ -422,8 +508,8 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
/* connections to link-local addresses must persist after changing
|
||||
the netif's address (RFC3927 ch. 1.9) */
|
||||
if ((netif->autoip != NULL) &&
|
||||
ip_addr_cmp(¤t_iphdr_dest, &(netif->autoip->llipaddr))) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n",
|
||||
ip4_addr_cmp(ip4_current_dest_addr(), &(netif->autoip->llipaddr))) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
|
||||
netif->name[0], netif->name[1]));
|
||||
/* break out of for loop */
|
||||
break;
|
||||
@@ -456,10 +542,10 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
/* remote port is DHCP server? */
|
||||
if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
|
||||
struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n",
|
||||
ntohs(udphdr->dest)));
|
||||
if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: DHCP packet accepted.\n"));
|
||||
netif = inp;
|
||||
check_ip_src = 0;
|
||||
}
|
||||
@@ -468,19 +554,24 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
|
||||
|
||||
/* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
|
||||
#if LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING
|
||||
if (check_ip_src
|
||||
#if IP_ACCEPT_LINK_LAYER_ADDRESSING
|
||||
/* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
|
||||
if (check_ip_src && !ip_addr_isany(¤t_iphdr_src))
|
||||
&& !ip4_addr_isany_val(*ip4_current_src_addr())
|
||||
#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
|
||||
{ if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) ||
|
||||
(ip_addr_ismulticast(¤t_iphdr_src))) {
|
||||
)
|
||||
#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */
|
||||
{
|
||||
if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
|
||||
(ip4_addr_ismulticast(ip4_current_src_addr()))) {
|
||||
/* packet source is not valid */
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip4_input: packet source is not valid.\n"));
|
||||
/* free (drop) packet pbufs */
|
||||
pbuf_free(p);
|
||||
IP_STATS_INC(ip.drop);
|
||||
snmp_inc_ipinaddrerrors();
|
||||
snmp_inc_ipindiscards();
|
||||
MIB2_STATS_INC(mib2.ipinaddrerrors);
|
||||
MIB2_STATS_INC(mib2.ipindiscards);
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
@@ -488,17 +579,17 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
/* packet not for us? */
|
||||
if (netif == NULL) {
|
||||
/* packet not for us, route or discard */
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
|
||||
#if IP_FORWARD
|
||||
/* non-broadcast packet? */
|
||||
if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) {
|
||||
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
|
||||
/* try to forward IP packet on (other) interfaces */
|
||||
ip_forward(p, iphdr, inp);
|
||||
ip4_forward(p, iphdr, inp);
|
||||
} else
|
||||
#endif /* IP_FORWARD */
|
||||
{
|
||||
snmp_inc_ipinaddrerrors();
|
||||
snmp_inc_ipindiscards();
|
||||
MIB2_STATS_INC(mib2.ipinaddrerrors);
|
||||
MIB2_STATS_INC(mib2.ipindiscards);
|
||||
}
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
@@ -506,10 +597,10 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
/* packet consists of multiple fragments? */
|
||||
if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
|
||||
#if IP_REASSEMBLY /* packet fragment reassembly code present? */
|
||||
LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
|
||||
ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip4_reass()\n",
|
||||
ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), (u16_t)!!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (u16_t)((ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)));
|
||||
/* reassemble the packet*/
|
||||
p = ip_reass(p);
|
||||
p = ip4_reass(p);
|
||||
/* packet not fully reassembled yet? */
|
||||
if (p == NULL) {
|
||||
return ERR_OK;
|
||||
@@ -522,7 +613,7 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
IP_STATS_INC(ip.opterr);
|
||||
IP_STATS_INC(ip.drop);
|
||||
/* unsupported protocol feature */
|
||||
snmp_inc_ipinunknownprotos();
|
||||
MIB2_STATS_INC(mib2.ipinunknownprotos);
|
||||
return ERR_OK;
|
||||
#endif /* IP_REASSEMBLY */
|
||||
}
|
||||
@@ -540,74 +631,82 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
IP_STATS_INC(ip.opterr);
|
||||
IP_STATS_INC(ip.drop);
|
||||
/* unsupported protocol feature */
|
||||
snmp_inc_ipinunknownprotos();
|
||||
MIB2_STATS_INC(mib2.ipinunknownprotos);
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* IP_OPTIONS_ALLOWED == 0 */
|
||||
|
||||
/* send to upper layers */
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
|
||||
ip_debug_print(p);
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_input: \n"));
|
||||
ip4_debug_print(p);
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
|
||||
|
||||
current_netif = inp;
|
||||
current_header = iphdr;
|
||||
ip_data.current_netif = netif;
|
||||
ip_data.current_input_netif = inp;
|
||||
ip_data.current_ip4_header = iphdr;
|
||||
ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4;
|
||||
|
||||
#if LWIP_RAW
|
||||
/* raw input did not eat the packet? */
|
||||
if (raw_input(p, inp) == 0)
|
||||
#endif /* LWIP_RAW */
|
||||
{
|
||||
pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */
|
||||
|
||||
switch (IPH_PROTO(iphdr)) {
|
||||
#if LWIP_UDP
|
||||
case IP_PROTO_UDP:
|
||||
#if LWIP_UDPLITE
|
||||
case IP_PROTO_UDPLITE:
|
||||
#endif /* LWIP_UDPLITE */
|
||||
snmp_inc_ipindelivers();
|
||||
MIB2_STATS_INC(mib2.ipindelivers);
|
||||
udp_input(p, inp);
|
||||
break;
|
||||
#endif /* LWIP_UDP */
|
||||
#if LWIP_TCP
|
||||
case IP_PROTO_TCP:
|
||||
snmp_inc_ipindelivers();
|
||||
MIB2_STATS_INC(mib2.ipindelivers);
|
||||
tcp_input(p, inp);
|
||||
break;
|
||||
#endif /* LWIP_TCP */
|
||||
#if LWIP_ICMP
|
||||
case IP_PROTO_ICMP:
|
||||
snmp_inc_ipindelivers();
|
||||
MIB2_STATS_INC(mib2.ipindelivers);
|
||||
icmp_input(p, inp);
|
||||
break;
|
||||
#endif /* LWIP_ICMP */
|
||||
#if LWIP_IGMP
|
||||
case IP_PROTO_IGMP:
|
||||
igmp_input(p, inp, ¤t_iphdr_dest);
|
||||
igmp_input(p, inp, ip4_current_dest_addr());
|
||||
break;
|
||||
#endif /* LWIP_IGMP */
|
||||
default:
|
||||
#if LWIP_ICMP
|
||||
/* send ICMP destination protocol unreachable unless is was a broadcast */
|
||||
if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) &&
|
||||
!ip_addr_ismulticast(¤t_iphdr_dest)) {
|
||||
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
|
||||
!ip4_addr_ismulticast(ip4_current_dest_addr())) {
|
||||
pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */
|
||||
p->payload = iphdr;
|
||||
icmp_dest_unreach(p, ICMP_DUR_PROTO);
|
||||
}
|
||||
#endif /* LWIP_ICMP */
|
||||
pbuf_free(p);
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr)));
|
||||
|
||||
IP_STATS_INC(ip.proterr);
|
||||
IP_STATS_INC(ip.drop);
|
||||
snmp_inc_ipinunknownprotos();
|
||||
MIB2_STATS_INC(mib2.ipinunknownprotos);
|
||||
}
|
||||
}
|
||||
|
||||
current_netif = NULL;
|
||||
current_header = NULL;
|
||||
ip_addr_set_any(¤t_iphdr_src);
|
||||
ip_addr_set_any(¤t_iphdr_dest);
|
||||
/* @todo: this is not really necessary... */
|
||||
ip_data.current_netif = NULL;
|
||||
ip_data.current_input_netif = NULL;
|
||||
ip_data.current_ip4_header = NULL;
|
||||
ip_data.current_ip_header_tot_len = 0;
|
||||
ip4_addr_set_any(ip4_current_src_addr());
|
||||
ip4_addr_set_any(ip4_current_dest_addr());
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
@@ -638,12 +737,12 @@ ip_input(struct pbuf *p, struct netif *inp)
|
||||
* unique identifiers independent of destination"
|
||||
*/
|
||||
err_t
|
||||
ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
|
||||
u8_t ttl, u8_t tos,
|
||||
u8_t proto, struct netif *netif)
|
||||
{
|
||||
#if IP_OPTIONS_SEND
|
||||
return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
|
||||
return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -652,22 +751,59 @@ ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
* @ param ip_options pointer to the IP options, copied into the IP header
|
||||
* @ param optlen length of ip_options
|
||||
*/
|
||||
err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
err_t
|
||||
ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
|
||||
u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
|
||||
u16_t optlen)
|
||||
{
|
||||
#endif /* IP_OPTIONS_SEND */
|
||||
const ip4_addr_t *src_used = src;
|
||||
if (dest != IP_HDRINCL) {
|
||||
if (ip4_addr_isany(src)) {
|
||||
src_used = netif_ip4_addr(netif);
|
||||
}
|
||||
}
|
||||
|
||||
#if IP_OPTIONS_SEND
|
||||
return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif,
|
||||
ip_options, optlen);
|
||||
#else /* IP_OPTIONS_SEND */
|
||||
return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);
|
||||
#endif /* IP_OPTIONS_SEND */
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as ip_output_if() but 'src' address is not replaced by netif address
|
||||
* when it is 'any'.
|
||||
*/
|
||||
err_t
|
||||
ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
|
||||
u8_t ttl, u8_t tos,
|
||||
u8_t proto, struct netif *netif)
|
||||
{
|
||||
#if IP_OPTIONS_SEND
|
||||
return ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as ip_output_if_opt() but 'src' address is not replaced by netif address
|
||||
* when it is 'any'.
|
||||
*/
|
||||
err_t
|
||||
ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
|
||||
u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
|
||||
u16_t optlen)
|
||||
{
|
||||
#endif /* IP_OPTIONS_SEND */
|
||||
struct ip_hdr *iphdr;
|
||||
ip_addr_t dest_addr;
|
||||
ip4_addr_t dest_addr;
|
||||
#if CHECKSUM_GEN_IP_INLINE
|
||||
u32_t chk_sum = 0;
|
||||
#endif /* CHECKSUM_GEN_IP_INLINE */
|
||||
|
||||
/* pbufs passed to IP must have a ref-count of 1 as their payload pointer
|
||||
gets altered as the packet is passed down the stack */
|
||||
LWIP_ASSERT("p->ref == 1", p->ref == 1);
|
||||
LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
|
||||
|
||||
snmp_inc_ipoutrequests();
|
||||
MIB2_STATS_INC(mib2.ipoutrequests);
|
||||
|
||||
/* Should the IP header be generated or is it already included in p? */
|
||||
if (dest != IP_HDRINCL) {
|
||||
@@ -683,9 +819,9 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
ip_hlen += optlen_aligned;
|
||||
/* First write in the IP options */
|
||||
if (pbuf_header(p, optlen_aligned)) {
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));
|
||||
IP_STATS_INC(ip.err);
|
||||
snmp_inc_ipoutdiscards();
|
||||
MIB2_STATS_INC(mib2.ipoutdiscards);
|
||||
return ERR_BUF;
|
||||
}
|
||||
MEMCPY(p->payload, ip_options, optlen);
|
||||
@@ -702,10 +838,10 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
#endif /* IP_OPTIONS_SEND */
|
||||
/* generate IP header */
|
||||
if (pbuf_header(p, IP_HLEN)) {
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n"));
|
||||
|
||||
IP_STATS_INC(ip.err);
|
||||
snmp_inc_ipoutdiscards();
|
||||
MIB2_STATS_INC(mib2.ipoutdiscards);
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
@@ -720,7 +856,7 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
#endif /* CHECKSUM_GEN_IP_INLINE */
|
||||
|
||||
/* dest cannot be NULL here */
|
||||
ip_addr_copy(iphdr->dest, *dest);
|
||||
ip4_addr_copy(iphdr->dest, *dest);
|
||||
#if CHECKSUM_GEN_IP_INLINE
|
||||
chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
|
||||
chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
|
||||
@@ -742,11 +878,11 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
#endif /* CHECKSUM_GEN_IP_INLINE */
|
||||
++ip_id;
|
||||
|
||||
if (ip_addr_isany(src)) {
|
||||
ip_addr_copy(iphdr->src, netif->ip_addr);
|
||||
if (src == NULL) {
|
||||
ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY);
|
||||
} else {
|
||||
/* src cannot be NULL here */
|
||||
ip_addr_copy(iphdr->src, *src);
|
||||
ip4_addr_copy(iphdr->src, *src);
|
||||
}
|
||||
|
||||
#if CHECKSUM_GEN_IP_INLINE
|
||||
@@ -755,45 +891,58 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
|
||||
chk_sum = (chk_sum >> 16) + chk_sum;
|
||||
chk_sum = ~chk_sum;
|
||||
iphdr->_chksum = chk_sum; /* network order */
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
|
||||
iphdr->_chksum = (u16_t)chk_sum; /* network order */
|
||||
}
|
||||
#if LWIP_CHECKSUM_CTRL_PER_NETIF
|
||||
else {
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
}
|
||||
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
|
||||
#else /* CHECKSUM_GEN_IP_INLINE */
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
#if CHECKSUM_GEN_IP
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
|
||||
#endif
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_IP */
|
||||
#endif /* CHECKSUM_GEN_IP_INLINE */
|
||||
} else {
|
||||
/* IP header already included in p */
|
||||
iphdr = (struct ip_hdr *)p->payload;
|
||||
ip_addr_copy(dest_addr, iphdr->dest);
|
||||
ip4_addr_copy(dest_addr, iphdr->dest);
|
||||
dest = &dest_addr;
|
||||
}
|
||||
|
||||
IP_STATS_INC(ip.xmit);
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
|
||||
ip_debug_print(p);
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
|
||||
ip4_debug_print(p);
|
||||
|
||||
#if ENABLE_LOOPBACK
|
||||
if (ip_addr_cmp(dest, &netif->ip_addr)) {
|
||||
if (ip4_addr_cmp(dest, netif_ip4_addr(netif))
|
||||
#if !LWIP_HAVE_LOOPIF
|
||||
|| ip4_addr_isloopback(dest)
|
||||
#endif /* !LWIP_HAVE_LOOPIF */
|
||||
) {
|
||||
/* Packet to self, enqueue it for loopback */
|
||||
LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
|
||||
return netif_loop_output(netif, p, dest);
|
||||
return netif_loop_output(netif, p);
|
||||
}
|
||||
#if LWIP_IGMP
|
||||
#if LWIP_MULTICAST_TX_OPTIONS
|
||||
if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
|
||||
netif_loop_output(netif, p, dest);
|
||||
netif_loop_output(netif, p);
|
||||
}
|
||||
#endif /* LWIP_IGMP */
|
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */
|
||||
#endif /* ENABLE_LOOPBACK */
|
||||
#if IP_FRAG
|
||||
/* don't fragment if interface has mtu set to 0 [loopif] */
|
||||
if (netif->mtu && (p->tot_len > netif->mtu)) {
|
||||
return ip_frag(p, netif, dest);
|
||||
return ip4_frag(p, netif, dest);
|
||||
}
|
||||
#endif /* IP_FRAG */
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("netif->output()"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));
|
||||
return netif->output(netif, p, dest);
|
||||
}
|
||||
|
||||
@@ -815,23 +964,21 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
* see ip_output_if() for more return values
|
||||
*/
|
||||
err_t
|
||||
ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
|
||||
u8_t ttl, u8_t tos, u8_t proto)
|
||||
{
|
||||
struct netif *netif;
|
||||
|
||||
/* pbufs passed to IP must have a ref-count of 1 as their payload pointer
|
||||
gets altered as the packet is passed down the stack */
|
||||
LWIP_ASSERT("p->ref == 1", p->ref == 1);
|
||||
LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
|
||||
|
||||
if ((netif = ip_route(dest)) == NULL) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
if ((netif = ip4_route_src(dest, src)) == NULL) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
|
||||
IP_STATS_INC(ip.rterr);
|
||||
return ERR_RTE;
|
||||
}
|
||||
|
||||
return ip_output_if(p, src, dest, ttl, tos, proto, netif);
|
||||
return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
|
||||
}
|
||||
|
||||
#if LWIP_NETIF_HWADDRHINT
|
||||
@@ -854,25 +1001,23 @@ ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
* see ip_output_if() for more return values
|
||||
*/
|
||||
err_t
|
||||
ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
|
||||
u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
|
||||
{
|
||||
struct netif *netif;
|
||||
err_t err;
|
||||
|
||||
/* pbufs passed to IP must have a ref-count of 1 as their payload pointer
|
||||
gets altered as the packet is passed down the stack */
|
||||
LWIP_ASSERT("p->ref == 1", p->ref == 1);
|
||||
LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
|
||||
|
||||
if ((netif = ip_route(dest)) == NULL) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
if ((netif = ip4_route_src(dest, src)) == NULL) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
|
||||
IP_STATS_INC(ip.rterr);
|
||||
return ERR_RTE;
|
||||
}
|
||||
|
||||
NETIF_SET_HWADDRHINT(netif, addr_hint);
|
||||
err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
|
||||
err = ip4_output_if(p, src, dest, ttl, tos, proto, netif);
|
||||
NETIF_SET_HWADDRHINT(netif, NULL);
|
||||
|
||||
return err;
|
||||
@@ -884,28 +1029,28 @@ ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
|
||||
* @param p an IP packet, p->payload pointing to the IP header
|
||||
*/
|
||||
void
|
||||
ip_debug_print(struct pbuf *p)
|
||||
ip4_debug_print(struct pbuf *p)
|
||||
{
|
||||
struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n",
|
||||
IPH_V(iphdr),
|
||||
IPH_HL(iphdr),
|
||||
IPH_TOS(iphdr),
|
||||
(u16_t)IPH_V(iphdr),
|
||||
(u16_t)IPH_HL(iphdr),
|
||||
(u16_t)IPH_TOS(iphdr),
|
||||
ntohs(IPH_LEN(iphdr))));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n",
|
||||
ntohs(IPH_ID(iphdr)),
|
||||
ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
|
||||
ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
|
||||
ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
|
||||
ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
|
||||
(u16_t)(ntohs(IPH_OFFSET(iphdr)) >> 15 & 1),
|
||||
(u16_t)(ntohs(IPH_OFFSET(iphdr)) >> 14 & 1),
|
||||
(u16_t)(ntohs(IPH_OFFSET(iphdr)) >> 13 & 1),
|
||||
(u16_t)(ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n",
|
||||
IPH_TTL(iphdr),
|
||||
IPH_PROTO(iphdr),
|
||||
(u16_t)IPH_TTL(iphdr),
|
||||
(u16_t)IPH_PROTO(iphdr),
|
||||
ntohs(IPH_CHKSUM(iphdr))));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n",
|
||||
@@ -922,3 +1067,5 @@ ip_debug_print(struct pbuf *p)
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
}
|
||||
#endif /* IP_DEBUG */
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
@@ -39,11 +39,13 @@
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/ip_frag.h"
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
#include "lwip/ip4_frag.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/icmp.h"
|
||||
|
||||
@@ -100,8 +102,8 @@ PACK_STRUCT_END
|
||||
#endif
|
||||
|
||||
#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
|
||||
(ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
|
||||
ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
|
||||
(ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
|
||||
ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
|
||||
IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
|
||||
|
||||
/* global variables */
|
||||
@@ -167,7 +169,7 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
|
||||
LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
|
||||
}
|
||||
|
||||
snmp_inc_ipreasmfails();
|
||||
MIB2_STATS_INC(mib2.ipreasmfails);
|
||||
#if LWIP_ICMP
|
||||
iprh = (struct ip_reass_helper *)ipr->p->payload;
|
||||
if (iprh->start == 0) {
|
||||
@@ -223,7 +225,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
|
||||
/* @todo Can't we simply remove the last datagram in the
|
||||
* linked list behind reassdatagrams?
|
||||
*/
|
||||
struct ip_reassdata *r, *oldest, *prev;
|
||||
struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
|
||||
int pbufs_freed = 0, pbufs_freed_current;
|
||||
int other_datagrams;
|
||||
|
||||
@@ -232,6 +234,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
|
||||
do {
|
||||
oldest = NULL;
|
||||
prev = NULL;
|
||||
oldest_prev = NULL;
|
||||
other_datagrams = 0;
|
||||
r = reassdatagrams;
|
||||
while (r != NULL) {
|
||||
@@ -240,9 +243,11 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
|
||||
other_datagrams++;
|
||||
if (oldest == NULL) {
|
||||
oldest = r;
|
||||
oldest_prev = prev;
|
||||
} else if (r->timer <= oldest->timer) {
|
||||
/* older than the previous oldest */
|
||||
oldest = r;
|
||||
oldest_prev = prev;
|
||||
}
|
||||
}
|
||||
if (r->next != NULL) {
|
||||
@@ -251,7 +256,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
|
||||
r = r->next;
|
||||
}
|
||||
if (oldest != NULL) {
|
||||
pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
|
||||
pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
|
||||
pbufs_freed += pbufs_freed_current;
|
||||
}
|
||||
} while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
|
||||
@@ -269,6 +274,10 @@ static struct ip_reassdata*
|
||||
ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
|
||||
{
|
||||
struct ip_reassdata* ipr;
|
||||
#if ! IP_REASS_FREE_OLDEST
|
||||
LWIP_UNUSED_ARG(clen);
|
||||
#endif
|
||||
|
||||
/* No matching previous fragment found, allocate a new reassdata struct */
|
||||
ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
|
||||
if (ipr == NULL) {
|
||||
@@ -303,7 +312,6 @@ ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
|
||||
static void
|
||||
ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||
{
|
||||
|
||||
/* dequeue the reass struct */
|
||||
if (reassdatagrams == ipr) {
|
||||
/* it was the first in the list */
|
||||
@@ -314,7 +322,7 @@ ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||
prev->next = ipr->next;
|
||||
}
|
||||
|
||||
/* now we can free the ip_reass struct */
|
||||
/* now we can free the ip_reassdata struct */
|
||||
memp_free(MEMP_REASSDATA, ipr);
|
||||
}
|
||||
|
||||
@@ -352,7 +360,7 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
|
||||
iprh->end = offset + len;
|
||||
|
||||
/* Iterate through until we either get to the end of the list (append),
|
||||
* or we find on with a larger offset (insert). */
|
||||
* or we find one with a larger offset (insert). */
|
||||
for (q = ipr->p; q != NULL;) {
|
||||
iprh_tmp = (struct ip_reass_helper*)q->payload;
|
||||
if (iprh->start < iprh_tmp->start) {
|
||||
@@ -381,7 +389,7 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
|
||||
goto freepbuf;
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
} else {
|
||||
/* Check if the fragments received so far have no wholes. */
|
||||
/* Check if the fragments received so far have no holes. */
|
||||
if (iprh_prev != NULL) {
|
||||
if (iprh_prev->end != iprh_tmp->start) {
|
||||
/* There is a fragment missing between the current
|
||||
@@ -419,14 +427,14 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
|
||||
/* At this point, the validation part begins: */
|
||||
/* If we already received the last fragment */
|
||||
if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
|
||||
/* and had no wholes so far */
|
||||
/* and had no holes so far */
|
||||
if (valid) {
|
||||
/* then check if the rest of the fragments is here */
|
||||
/* Check if the queue starts with the first datagram */
|
||||
if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
|
||||
if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) {
|
||||
valid = 0;
|
||||
} else {
|
||||
/* and check that there are no wholes after this datagram */
|
||||
/* and check that there are no holes after this datagram */
|
||||
iprh_prev = iprh;
|
||||
q = iprh->next_pbuf;
|
||||
while (q != NULL) {
|
||||
@@ -473,7 +481,7 @@ freepbuf:
|
||||
* @return NULL if reassembly is incomplete, ? otherwise
|
||||
*/
|
||||
struct pbuf *
|
||||
ip_reass(struct pbuf *p)
|
||||
ip4_reass(struct pbuf *p)
|
||||
{
|
||||
struct pbuf *r;
|
||||
struct ip_hdr *fraghdr;
|
||||
@@ -481,15 +489,14 @@ ip_reass(struct pbuf *p)
|
||||
struct ip_reass_helper *iprh;
|
||||
u16_t offset, len;
|
||||
u8_t clen;
|
||||
struct ip_reassdata *ipr_prev = NULL;
|
||||
|
||||
IPFRAG_STATS_INC(ip_frag.recv);
|
||||
snmp_inc_ipreasmreqds();
|
||||
MIB2_STATS_INC(mib2.ipreasmreqds);
|
||||
|
||||
fraghdr = (struct ip_hdr*)p->payload;
|
||||
|
||||
if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: IP options currently not supported!\n"));
|
||||
IPFRAG_STATS_INC(ip_frag.err);
|
||||
goto nullreturn;
|
||||
}
|
||||
@@ -506,7 +513,7 @@ ip_reass(struct pbuf *p)
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
/* No datagram could be freed and still too many pbufs enqueued */
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
|
||||
ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
|
||||
IPFRAG_STATS_INC(ip_frag.memerr);
|
||||
/* @todo: send ICMP time exceeded here? */
|
||||
@@ -522,12 +529,11 @@ ip_reass(struct pbuf *p)
|
||||
in the reassembly buffer. If so, we proceed with copying the
|
||||
fragment into the buffer. */
|
||||
if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n",
|
||||
ntohs(IPH_ID(fraghdr))));
|
||||
IPFRAG_STATS_INC(ip_frag.cachehit);
|
||||
break;
|
||||
}
|
||||
ipr_prev = ipr;
|
||||
}
|
||||
|
||||
if (ipr == NULL) {
|
||||
@@ -559,12 +565,13 @@ ip_reass(struct pbuf *p)
|
||||
ipr->flags |= IP_REASS_FLAG_LASTFRAG;
|
||||
ipr->datagram_len = offset + len;
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: last fragment seen, total len %"S16_F"\n",
|
||||
("ip4_reass: last fragment seen, total len %"S16_F"\n",
|
||||
ipr->datagram_len));
|
||||
}
|
||||
/* find the right place to insert this pbuf */
|
||||
/* @todo: trim pbufs if fragments are overlapping */
|
||||
if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
|
||||
struct ip_reassdata *ipr_prev;
|
||||
/* the totally last fragment (flag more fragments = 0) was received at least
|
||||
* once AND all fragments are received */
|
||||
ipr->datagram_len += IP_HLEN;
|
||||
@@ -578,8 +585,12 @@ ip_reass(struct pbuf *p)
|
||||
IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
|
||||
IPH_OFFSET_SET(fraghdr, 0);
|
||||
IPH_CHKSUM_SET(fraghdr, 0);
|
||||
/* @todo: do we need to set calculate the correct checksum? */
|
||||
/* @todo: do we need to set/calculate the correct checksum? */
|
||||
#if CHECKSUM_GEN_IP
|
||||
IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) {
|
||||
IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_IP */
|
||||
|
||||
p = ipr->p;
|
||||
|
||||
@@ -587,17 +598,31 @@ ip_reass(struct pbuf *p)
|
||||
while (r != NULL) {
|
||||
iprh = (struct ip_reass_helper*)r->payload;
|
||||
|
||||
/* hide the ip header for every succeding fragment */
|
||||
/* hide the ip header for every succeeding fragment */
|
||||
pbuf_header(r, -IP_HLEN);
|
||||
pbuf_cat(p, r);
|
||||
r = iprh->next_pbuf;
|
||||
}
|
||||
|
||||
/* find the previous entry in the linked list */
|
||||
if (ipr == reassdatagrams) {
|
||||
ipr_prev = NULL;
|
||||
} else {
|
||||
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
|
||||
if (ipr_prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* release the sources allocate for the fragment queue entry */
|
||||
ip_reass_dequeue_datagram(ipr, ipr_prev);
|
||||
|
||||
/* and adjust the number of pbufs currently queued for reassembly. */
|
||||
ip_reass_pbufcount -= pbuf_clen(p);
|
||||
|
||||
MIB2_STATS_INC(mib2.ipreasmoks);
|
||||
|
||||
/* Return the pbuf chain */
|
||||
return p;
|
||||
}
|
||||
@@ -606,7 +631,7 @@ ip_reass(struct pbuf *p)
|
||||
return NULL;
|
||||
|
||||
nullreturn:
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: nullreturn\n"));
|
||||
IPFRAG_STATS_INC(ip_frag.drop);
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
@@ -615,7 +640,7 @@ nullreturn:
|
||||
|
||||
#if IP_FRAG
|
||||
#if IP_FRAG_USES_STATIC_BUF
|
||||
static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
|
||||
static LWIP_DECLARE_MEMORY_ALIGNED(buf, IP_FRAG_MAX_MTU);
|
||||
#else /* IP_FRAG_USES_STATIC_BUF */
|
||||
|
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF
|
||||
@@ -664,7 +689,7 @@ ipfrag_free_pbuf_custom(struct pbuf *p)
|
||||
* @return ERR_OK if sent successfully, err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
|
||||
{
|
||||
struct pbuf *rambuf;
|
||||
#if IP_FRAG_USES_STATIC_BUF
|
||||
@@ -697,7 +722,7 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
|
||||
if (rambuf == NULL) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
|
||||
return ERR_MEM;
|
||||
goto memerr;
|
||||
}
|
||||
rambuf->tot_len = rambuf->len = mtu;
|
||||
rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
|
||||
@@ -737,7 +762,7 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
#if LWIP_NETIF_TX_SINGLE_PBUF
|
||||
rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
|
||||
if (rambuf == NULL) {
|
||||
return ERR_MEM;
|
||||
goto memerr;
|
||||
}
|
||||
LWIP_ASSERT("this needs a pbuf in one piece!",
|
||||
(rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
|
||||
@@ -745,11 +770,11 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
/* make room for the IP header */
|
||||
if (pbuf_header(rambuf, IP_HLEN)) {
|
||||
pbuf_free(rambuf);
|
||||
return ERR_MEM;
|
||||
goto memerr;
|
||||
}
|
||||
/* fill in the IP header */
|
||||
SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
|
||||
iphdr = rambuf->payload;
|
||||
iphdr = (struct ip_hdr*)rambuf->payload;
|
||||
#else /* LWIP_NETIF_TX_SINGLE_PBUF */
|
||||
/* When not using a static buffer, create a chain of pbufs.
|
||||
* The first will be a PBUF_RAM holding the link and IP header.
|
||||
@@ -758,7 +783,7 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
*/
|
||||
rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
|
||||
if (rambuf == NULL) {
|
||||
return ERR_MEM;
|
||||
goto memerr;
|
||||
}
|
||||
LWIP_ASSERT("this needs a pbuf in one piece!",
|
||||
(p->len >= (IP_HLEN)));
|
||||
@@ -781,14 +806,14 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
pcr = ip_frag_alloc_pbuf_custom_ref();
|
||||
if (pcr == NULL) {
|
||||
pbuf_free(rambuf);
|
||||
return ERR_MEM;
|
||||
goto memerr;
|
||||
}
|
||||
/* Mirror this pbuf, although we might not need all of it. */
|
||||
newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
|
||||
if (newpbuf == NULL) {
|
||||
ip_frag_free_pbuf_custom_ref(pcr);
|
||||
pbuf_free(rambuf);
|
||||
return ERR_MEM;
|
||||
goto memerr;
|
||||
}
|
||||
pbuf_ref(p);
|
||||
pcr->original = p;
|
||||
@@ -811,7 +836,11 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
IPH_OFFSET_SET(iphdr, htons(tmp));
|
||||
IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
#if CHECKSUM_GEN_IP
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_IP */
|
||||
|
||||
#if IP_FRAG_USES_STATIC_BUF
|
||||
if (last) {
|
||||
@@ -828,12 +857,12 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
pbuf_chain(header, rambuf);
|
||||
netif->output(netif, header, dest);
|
||||
IPFRAG_STATS_INC(ip_frag.xmit);
|
||||
snmp_inc_ipfragcreates();
|
||||
MIB2_STATS_INC(mib2.ipfragcreates);
|
||||
pbuf_free(header);
|
||||
} else {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
|
||||
pbuf_free(rambuf);
|
||||
return ERR_MEM;
|
||||
goto memerr;
|
||||
}
|
||||
#else /* IP_FRAG_USES_STATIC_BUF */
|
||||
/* No need for separate header pbuf - we allowed room for it in rambuf
|
||||
@@ -857,7 +886,12 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
|
||||
#if IP_FRAG_USES_STATIC_BUF
|
||||
pbuf_free(rambuf);
|
||||
#endif /* IP_FRAG_USES_STATIC_BUF */
|
||||
snmp_inc_ipfragoks();
|
||||
MIB2_STATS_INC(mib2.ipfragoks);
|
||||
return ERR_OK;
|
||||
memerr:
|
||||
MIB2_STATS_INC(mib2.ipfragfails);
|
||||
return ERR_MEM;
|
||||
}
|
||||
#endif /* IP_FRAG */
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
157
ext/lwip/src/core/ipv6/ethip6.c
Normal file
157
ext/lwip/src/core/ipv6/ethip6.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Ethernet output for IPv6. Uses ND tables for link-layer addressing.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. 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: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
|
||||
|
||||
#include "lwip/ethip6.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/icmp6.h"
|
||||
#include "netif/ethernet.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Send an IPv6 packet on the network using netif->linkoutput
|
||||
* The ethernet header is filled in before sending.
|
||||
*
|
||||
* @params netif the lwIP network interface on which to send the packet
|
||||
* @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
|
||||
* @params src the source MAC address to be copied into the ethernet header
|
||||
* @params dst the destination MAC address to be copied into the ethernet header
|
||||
* @return ERR_OK if the packet was sent, any other err_t on failure
|
||||
*/
|
||||
static err_t
|
||||
ethip6_send(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
|
||||
{
|
||||
struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
|
||||
|
||||
LWIP_ASSERT("netif->hwaddr_len must be 6 for ethip6!",
|
||||
(netif->hwaddr_len == 6));
|
||||
SMEMCPY(ðhdr->dest, dst, 6);
|
||||
SMEMCPY(ðhdr->src, src, 6);
|
||||
ethhdr->type = PP_HTONS(ETHTYPE_IPV6);
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p));
|
||||
/* send the packet */
|
||||
return netif->linkoutput(netif, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve and fill-in Ethernet address header for outgoing IPv6 packet.
|
||||
*
|
||||
* For IPv6 multicast, corresponding Ethernet addresses
|
||||
* are selected and the packet is transmitted on the link.
|
||||
*
|
||||
* For unicast addresses, ...
|
||||
*
|
||||
* @todo anycast addresses
|
||||
*
|
||||
* @param netif The lwIP network interface which the IP packet will be sent on.
|
||||
* @param q The pbuf(s) containing the IP packet to be sent.
|
||||
* @param ip6addr The IP address of the packet destination.
|
||||
*
|
||||
* @return
|
||||
* - ERR_RTE No route to destination (no gateway to external networks),
|
||||
* or the return type of either etharp_query() or etharp_send_ip().
|
||||
*/
|
||||
err_t
|
||||
ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
|
||||
{
|
||||
struct eth_addr dest;
|
||||
s8_t i;
|
||||
|
||||
/* make room for Ethernet header - should not fail */
|
||||
if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
|
||||
/* bail out */
|
||||
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
|
||||
("etharp_output: could not allocate room for header.\n"));
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
/* multicast destination IP address? */
|
||||
if (ip6_addr_ismulticast(ip6addr)) {
|
||||
/* Hash IP multicast address to MAC address.*/
|
||||
dest.addr[0] = 0x33;
|
||||
dest.addr[1] = 0x33;
|
||||
dest.addr[2] = ((const u8_t *)(&(ip6addr->addr[3])))[0];
|
||||
dest.addr[3] = ((const u8_t *)(&(ip6addr->addr[3])))[1];
|
||||
dest.addr[4] = ((const u8_t *)(&(ip6addr->addr[3])))[2];
|
||||
dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
|
||||
|
||||
/* Send out. */
|
||||
return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest);
|
||||
}
|
||||
|
||||
/* We have a unicast destination IP address */
|
||||
/* @todo anycast? */
|
||||
/* Get next hop record. */
|
||||
i = nd6_get_next_hop_entry(ip6addr, netif);
|
||||
if (i < 0) {
|
||||
/* failed to get a next hop neighbor record. */
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Now that we have a destination record, send or queue the packet. */
|
||||
if (neighbor_cache[i].state == ND6_STALE) {
|
||||
/* Switch to delay state. */
|
||||
neighbor_cache[i].state = ND6_DELAY;
|
||||
neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
|
||||
}
|
||||
/* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
|
||||
if ((neighbor_cache[i].state == ND6_REACHABLE) ||
|
||||
(neighbor_cache[i].state == ND6_DELAY) ||
|
||||
(neighbor_cache[i].state == ND6_PROBE)) {
|
||||
|
||||
/* Send out. */
|
||||
SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6);
|
||||
return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest);
|
||||
}
|
||||
/* We should queue packet on this interface. */
|
||||
pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR);
|
||||
return nd6_queue_packet(i, q);
|
||||
}
|
||||
|
||||
349
ext/lwip/src/core/ipv6/icmp6.c
Normal file
349
ext/lwip/src/core/ipv6/icmp6.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* IPv6 version of ICMP, as per RFC 4443.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. 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: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/icmp6.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifndef LWIP_ICMP6_DATASIZE
|
||||
#define LWIP_ICMP6_DATASIZE 8
|
||||
#endif
|
||||
#if LWIP_ICMP6_DATASIZE == 0
|
||||
#define LWIP_ICMP6_DATASIZE 8
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
|
||||
|
||||
|
||||
/**
|
||||
* Process an input ICMPv6 message. Called by ip6_input.
|
||||
*
|
||||
* Will generate a reply for echo requests. Other messages are forwarded
|
||||
* to nd6_input, or mld6_input.
|
||||
*
|
||||
* @param p the mld packet, p->payload pointing to the icmpv6 header
|
||||
* @param inp the netif on which this packet was received
|
||||
*/
|
||||
void
|
||||
icmp6_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
struct icmp6_hdr *icmp6hdr;
|
||||
struct pbuf * r;
|
||||
const ip6_addr_t * reply_src;
|
||||
|
||||
ICMP6_STATS_INC(icmp6.recv);
|
||||
|
||||
/* Check that ICMPv6 header fits in payload */
|
||||
if (p->len < sizeof(struct icmp6_hdr)) {
|
||||
/* drop short packets */
|
||||
pbuf_free(p);
|
||||
ICMP6_STATS_INC(icmp6.lenerr);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
return;
|
||||
}
|
||||
|
||||
icmp6hdr = (struct icmp6_hdr *)p->payload;
|
||||
|
||||
#if CHECKSUM_CHECK_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
|
||||
if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
|
||||
ip6_current_dest_addr()) != 0) {
|
||||
/* Checksum failed */
|
||||
pbuf_free(p);
|
||||
ICMP6_STATS_INC(icmp6.chkerr);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif /* CHECKSUM_CHECK_ICMP6 */
|
||||
|
||||
switch (icmp6hdr->type) {
|
||||
case ICMP6_TYPE_NA: /* Neighbor advertisement */
|
||||
case ICMP6_TYPE_NS: /* Neighbor solicitation */
|
||||
case ICMP6_TYPE_RA: /* Router advertisement */
|
||||
case ICMP6_TYPE_RD: /* Redirect */
|
||||
case ICMP6_TYPE_PTB: /* Packet too big */
|
||||
nd6_input(p, inp);
|
||||
return;
|
||||
break;
|
||||
case ICMP6_TYPE_RS:
|
||||
#if LWIP_IPV6_FORWARD
|
||||
/* @todo implement router functionality */
|
||||
#endif
|
||||
break;
|
||||
#if LWIP_IPV6_MLD
|
||||
case ICMP6_TYPE_MLQ:
|
||||
case ICMP6_TYPE_MLR:
|
||||
case ICMP6_TYPE_MLD:
|
||||
mld6_input(p, inp);
|
||||
return;
|
||||
break;
|
||||
#endif
|
||||
case ICMP6_TYPE_EREQ:
|
||||
#if !LWIP_MULTICAST_PING
|
||||
/* multicast destination address? */
|
||||
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
return;
|
||||
}
|
||||
#endif /* LWIP_MULTICAST_PING */
|
||||
|
||||
/* Allocate reply. */
|
||||
r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
|
||||
if (r == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
ICMP6_STATS_INC(icmp6.memerr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy echo request. */
|
||||
if (pbuf_copy(r, p) != ERR_OK) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
pbuf_free(r);
|
||||
ICMP6_STATS_INC(icmp6.err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine reply source IPv6 address. */
|
||||
#if LWIP_MULTICAST_PING
|
||||
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
|
||||
reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
|
||||
if (reply_src == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
pbuf_free(r);
|
||||
ICMP6_STATS_INC(icmp6.rterr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* LWIP_MULTICAST_PING */
|
||||
{
|
||||
reply_src = ip6_current_dest_addr();
|
||||
}
|
||||
|
||||
/* Set fields in reply. */
|
||||
((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
|
||||
((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
|
||||
#if CHECKSUM_GEN_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
|
||||
((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
|
||||
IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_ICMP6 */
|
||||
|
||||
/* Send reply. */
|
||||
ICMP6_STATS_INC(icmp6.xmit);
|
||||
ip6_output_if(r, reply_src, ip6_current_src_addr(),
|
||||
LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
|
||||
pbuf_free(r);
|
||||
|
||||
break;
|
||||
default:
|
||||
ICMP6_STATS_INC(icmp6.proterr);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'destination unreachable' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'unreachable' should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param c ICMPv6 code for the unreachable type
|
||||
*/
|
||||
void
|
||||
icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
|
||||
{
|
||||
icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'packet too big' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'packet too big' should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param mtu the maximum mtu that we can accept
|
||||
*/
|
||||
void
|
||||
icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
|
||||
{
|
||||
icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'time exceeded' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'unreachable' should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param c ICMPv6 code for the time exceeded type
|
||||
*/
|
||||
void
|
||||
icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
|
||||
{
|
||||
icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'parameter problem' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'param problem' should be sent,
|
||||
* p->payload pointing to the IP header
|
||||
* @param c ICMPv6 code for the param problem type
|
||||
* @param pointer the pointer to the byte where the parameter is found
|
||||
*/
|
||||
void
|
||||
icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
|
||||
{
|
||||
icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ICMPv6 packet in response to an incoming packet.
|
||||
*
|
||||
* @param p the input packet for which the response should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param code Code of the ICMPv6 header
|
||||
* @param data Additional 32-bit parameter in the ICMPv6 header
|
||||
* @param type Type of the ICMPv6 header
|
||||
*/
|
||||
static void
|
||||
icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct icmp6_hdr *icmp6hdr;
|
||||
const ip6_addr_t *reply_src;
|
||||
ip6_addr_t *reply_dest;
|
||||
ip6_addr_t reply_src_local, reply_dest_local;
|
||||
struct ip6_hdr *ip6hdr;
|
||||
struct netif *netif;
|
||||
|
||||
/* ICMPv6 header + IPv6 header + data */
|
||||
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
|
||||
PBUF_RAM);
|
||||
if (q == NULL) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
|
||||
ICMP6_STATS_INC(icmp6.memerr);
|
||||
return;
|
||||
}
|
||||
LWIP_ASSERT("check that first pbuf can hold icmp 6message",
|
||||
(q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
|
||||
|
||||
icmp6hdr = (struct icmp6_hdr *)q->payload;
|
||||
icmp6hdr->type = type;
|
||||
icmp6hdr->code = code;
|
||||
icmp6hdr->data = data;
|
||||
|
||||
/* copy fields from original packet */
|
||||
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
|
||||
IP6_HLEN + LWIP_ICMP6_DATASIZE);
|
||||
|
||||
/* Get the destination address and netif for this ICMP message. */
|
||||
if ((ip_current_netif() == NULL) ||
|
||||
((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
|
||||
/* Special case, as ip6_current_xxx is either NULL, or points
|
||||
* to a different packet than the one that expired.
|
||||
* We must use the addresses that are stored in the expired packet. */
|
||||
ip6hdr = (struct ip6_hdr *)p->payload;
|
||||
/* copy from packed address to aligned address */
|
||||
ip6_addr_copy(reply_dest_local, ip6hdr->src);
|
||||
ip6_addr_copy(reply_src_local, ip6hdr->dest);
|
||||
reply_dest = &reply_dest_local;
|
||||
reply_src = &reply_src_local;
|
||||
netif = ip6_route(reply_src, reply_dest);
|
||||
if (netif == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(q);
|
||||
ICMP6_STATS_INC(icmp6.rterr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
netif = ip_current_netif();
|
||||
reply_dest = ip6_current_src_addr();
|
||||
|
||||
/* Select an address to use as source. */
|
||||
reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
|
||||
if (reply_src == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(q);
|
||||
ICMP6_STATS_INC(icmp6.rterr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* calculate checksum */
|
||||
icmp6hdr->chksum = 0;
|
||||
#if CHECKSUM_GEN_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
|
||||
icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
|
||||
reply_src, reply_dest);
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_ICMP6 */
|
||||
|
||||
ICMP6_STATS_INC(icmp6.xmit);
|
||||
ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
|
||||
pbuf_free(q);
|
||||
}
|
||||
|
||||
#endif /* LWIP_ICMP6 && LWIP_IPV6 */
|
||||
1122
ext/lwip/src/core/ipv6/ip6.c
Normal file
1122
ext/lwip/src/core/ipv6/ip6.c
Normal file
File diff suppressed because it is too large
Load Diff
292
ext/lwip/src/core/ipv6/ip6_addr.c
Normal file
292
ext/lwip/src/core/ipv6/ip6_addr.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* IPv6 addresses.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. 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: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
* Functions for handling IPv6 addresses.
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/def.h"
|
||||
|
||||
/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
|
||||
const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
|
||||
|
||||
#ifndef isprint
|
||||
#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
|
||||
#define isprint(c) in_range(c, 0x20, 0x7f)
|
||||
#define isdigit(c) in_range(c, '0', '9')
|
||||
#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
|
||||
#define islower(c) in_range(c, 'a', 'z')
|
||||
#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
|
||||
#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check whether "cp" is a valid ascii representation
|
||||
* of an IPv6 address and convert to a binary address.
|
||||
* Returns 1 if the address is valid, 0 if not.
|
||||
*
|
||||
* @param cp IPv6 address in ascii representation (e.g. "FF01::1")
|
||||
* @param addr pointer to which to save the ip address in network order
|
||||
* @return 1 if cp could be converted to addr, 0 on failure
|
||||
*/
|
||||
int
|
||||
ip6addr_aton(const char *cp, ip6_addr_t *addr)
|
||||
{
|
||||
u32_t addr_index, zero_blocks, current_block_index, current_block_value;
|
||||
const char * s;
|
||||
|
||||
/* Count the number of colons, to count the number of blocks in a "::" sequence
|
||||
zero_blocks may be 1 even if there are no :: sequences */
|
||||
zero_blocks = 8;
|
||||
for (s = cp; *s != 0; s++) {
|
||||
if (*s == ':') {
|
||||
zero_blocks--;
|
||||
} else if (!isxdigit(*s)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse each block */
|
||||
addr_index = 0;
|
||||
current_block_index = 0;
|
||||
current_block_value = 0;
|
||||
for (s = cp; *s != 0; s++) {
|
||||
if (*s == ':') {
|
||||
if (addr) {
|
||||
if (current_block_index & 0x1) {
|
||||
addr->addr[addr_index++] |= current_block_value;
|
||||
}
|
||||
else {
|
||||
addr->addr[addr_index] = current_block_value << 16;
|
||||
}
|
||||
}
|
||||
current_block_index++;
|
||||
current_block_value = 0;
|
||||
if (current_block_index > 7) {
|
||||
/* address too long! */
|
||||
return 0;
|
||||
}
|
||||
if (s[1] == ':') {
|
||||
if (s[2] == ':') {
|
||||
/* invalid format: three successive colons */
|
||||
return 0;
|
||||
}
|
||||
s++;
|
||||
/* "::" found, set zeros */
|
||||
while (zero_blocks > 0) {
|
||||
zero_blocks--;
|
||||
if (current_block_index & 0x1) {
|
||||
addr_index++;
|
||||
} else {
|
||||
if (addr) {
|
||||
addr->addr[addr_index] = 0;
|
||||
}
|
||||
}
|
||||
current_block_index++;
|
||||
if (current_block_index > 7) {
|
||||
/* address too long! */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isxdigit(*s)) {
|
||||
/* add current digit */
|
||||
current_block_value = (current_block_value << 4) +
|
||||
(isdigit(*s) ? *s - '0' :
|
||||
10 + (islower(*s) ? *s - 'a' : *s - 'A'));
|
||||
} else {
|
||||
/* unexpected digit, space? CRLF? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr) {
|
||||
if (current_block_index & 0x1) {
|
||||
addr->addr[addr_index++] |= current_block_value;
|
||||
}
|
||||
else {
|
||||
addr->addr[addr_index] = current_block_value << 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert to network byte order. */
|
||||
if (addr) {
|
||||
for (addr_index = 0; addr_index < 4; addr_index++) {
|
||||
addr->addr[addr_index] = htonl(addr->addr[addr_index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (current_block_index != 7) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert numeric IPv6 address into ASCII representation.
|
||||
* returns ptr to static buffer; not reentrant!
|
||||
*
|
||||
* @param addr ip6 address in network order to convert
|
||||
* @return pointer to a global static (!) buffer that holds the ASCII
|
||||
* representation of addr
|
||||
*/
|
||||
char *
|
||||
ip6addr_ntoa(const ip6_addr_t *addr)
|
||||
{
|
||||
static char str[40];
|
||||
return ip6addr_ntoa_r(addr, str, 40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
|
||||
*
|
||||
* @param addr ip6 address in network order to convert
|
||||
* @param buf target buffer where the string is stored
|
||||
* @param buflen length of buf
|
||||
* @return either pointer to buf which now holds the ASCII
|
||||
* representation of addr or NULL if buf was too small
|
||||
*/
|
||||
char *
|
||||
ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
|
||||
{
|
||||
u32_t current_block_index, current_block_value, next_block_value;
|
||||
s32_t i;
|
||||
u8_t zero_flag, empty_block_flag;
|
||||
|
||||
i = 0;
|
||||
empty_block_flag = 0; /* used to indicate a zero chain for "::' */
|
||||
|
||||
for (current_block_index = 0; current_block_index < 8; current_block_index++) {
|
||||
/* get the current 16-bit block */
|
||||
current_block_value = htonl(addr->addr[current_block_index >> 1]);
|
||||
if ((current_block_index & 0x1) == 0) {
|
||||
current_block_value = current_block_value >> 16;
|
||||
}
|
||||
current_block_value &= 0xffff;
|
||||
|
||||
/* Check for empty block. */
|
||||
if (current_block_value == 0) {
|
||||
if (current_block_index == 7 && empty_block_flag == 1) {
|
||||
/* special case, we must render a ':' for the last block. */
|
||||
buf[i++] = ':';
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (empty_block_flag == 0) {
|
||||
/* generate empty block "::", but only if more than one contiguous zero block,
|
||||
* according to current formatting suggestions RFC 5952. */
|
||||
next_block_value = htonl(addr->addr[(current_block_index + 1) >> 1]);
|
||||
if ((current_block_index & 0x1) == 0x01) {
|
||||
next_block_value = next_block_value >> 16;
|
||||
}
|
||||
next_block_value &= 0xffff;
|
||||
if (next_block_value == 0) {
|
||||
empty_block_flag = 1;
|
||||
buf[i++] = ':';
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
continue; /* move on to next block. */
|
||||
}
|
||||
} else if (empty_block_flag == 1) {
|
||||
/* move on to next block. */
|
||||
continue;
|
||||
}
|
||||
} else if (empty_block_flag == 1) {
|
||||
/* Set this flag value so we don't produce multiple empty blocks. */
|
||||
empty_block_flag = 2;
|
||||
}
|
||||
|
||||
if (current_block_index > 0) {
|
||||
buf[i++] = ':';
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((current_block_value & 0xf000) == 0) {
|
||||
zero_flag = 1;
|
||||
} else {
|
||||
buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
|
||||
zero_flag = 0;
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
buf[i++] = xchar(((current_block_value & 0xf00) >> 8));
|
||||
zero_flag = 0;
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
|
||||
/* do nothing */
|
||||
}
|
||||
else {
|
||||
buf[i++] = xchar(((current_block_value & 0xf0) >> 4));
|
||||
zero_flag = 0;
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
buf[i++] = xchar((current_block_value & 0xf));
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
buf[i] = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 */
|
||||
776
ext/lwip/src/core/ipv6/ip6_frag.c
Normal file
776
ext/lwip/src/core/ipv6/ip6_frag.c
Normal file
@@ -0,0 +1,776 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* IPv6 fragmentation and reassembly.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. 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: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/ip6_frag.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/icmp6.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/ip.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
|
||||
/** Setting this to 0, you can turn off checking the fragments for overlapping
|
||||
* regions. The code gets a little smaller. Only use this if you know that
|
||||
* overlapping won't occur on your network! */
|
||||
#ifndef IP_REASS_CHECK_OVERLAP
|
||||
#define IP_REASS_CHECK_OVERLAP 1
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
|
||||
/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
|
||||
* full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
|
||||
* Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
|
||||
* is set to 1, so one datagram can be reassembled at a time, only. */
|
||||
#ifndef IP_REASS_FREE_OLDEST
|
||||
#define IP_REASS_FREE_OLDEST 1
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
|
||||
#endif
|
||||
|
||||
#define IP_REASS_FLAG_LASTFRAG 0x01
|
||||
|
||||
/** This is a helper struct which holds the starting
|
||||
* offset and the ending offset of this fragment to
|
||||
* easily chain the fragments.
|
||||
* It has the same packing requirements as the IPv6 header, since it replaces
|
||||
* the Fragment Header in memory in incoming fragments to keep
|
||||
* track of the various fragments.
|
||||
*/
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct ip6_reass_helper {
|
||||
PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
|
||||
PACK_STRUCT_FIELD(u16_t start);
|
||||
PACK_STRUCT_FIELD(u16_t end);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
/* static variables */
|
||||
static struct ip6_reassdata *reassdatagrams;
|
||||
static u16_t ip6_reass_pbufcount;
|
||||
|
||||
/* Forward declarations. */
|
||||
static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
void
|
||||
ip6_reass_tmr(void)
|
||||
{
|
||||
struct ip6_reassdata *r, *tmp;
|
||||
|
||||
#if !IPV6_FRAG_COPYHEADER
|
||||
LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
|
||||
sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
|
||||
#endif /* !IPV6_FRAG_COPYHEADER */
|
||||
|
||||
r = reassdatagrams;
|
||||
while (r != NULL) {
|
||||
/* Decrement the timer. Once it reaches 0,
|
||||
* clean up the incomplete fragment assembly */
|
||||
if (r->timer > 0) {
|
||||
r->timer--;
|
||||
r = r->next;
|
||||
} else {
|
||||
/* reassembly timed out */
|
||||
tmp = r;
|
||||
/* get the next pointer before freeing */
|
||||
r = r->next;
|
||||
/* free the helper struct and all enqueued pbufs */
|
||||
ip6_reass_free_complete_datagram(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a datagram (struct ip6_reassdata) and all its pbufs.
|
||||
* Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
|
||||
* sends an ICMP time exceeded packet.
|
||||
*
|
||||
* @param ipr datagram to free
|
||||
*/
|
||||
static void
|
||||
ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
|
||||
{
|
||||
struct ip6_reassdata *prev;
|
||||
u16_t pbufs_freed = 0;
|
||||
u8_t clen;
|
||||
struct pbuf *p;
|
||||
struct ip6_reass_helper *iprh;
|
||||
|
||||
#if LWIP_ICMP6
|
||||
iprh = (struct ip6_reass_helper *)ipr->p->payload;
|
||||
if (iprh->start == 0) {
|
||||
/* The first fragment was received, send ICMP time exceeded. */
|
||||
/* First, de-queue the first pbuf from r->p. */
|
||||
p = ipr->p;
|
||||
ipr->p = iprh->next_pbuf;
|
||||
/* Then, move back to the original ipv6 header (we are now pointing to Fragment header).
|
||||
This cannot fail since we already checked when receiving this fragment. */
|
||||
if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)IPV6_FRAG_HDRREF(ipr->iphdr)))) {
|
||||
LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
|
||||
}
|
||||
else {
|
||||
icmp6_time_exceeded(p, ICMP6_TE_FRAG);
|
||||
}
|
||||
clen = pbuf_clen(p);
|
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
|
||||
pbufs_freed += clen;
|
||||
pbuf_free(p);
|
||||
}
|
||||
#endif /* LWIP_ICMP6 */
|
||||
|
||||
/* First, free all received pbufs. The individual pbufs need to be released
|
||||
separately as they have not yet been chained */
|
||||
p = ipr->p;
|
||||
while (p != NULL) {
|
||||
struct pbuf *pcur;
|
||||
iprh = (struct ip6_reass_helper *)p->payload;
|
||||
pcur = p;
|
||||
/* get the next pointer before freeing */
|
||||
p = iprh->next_pbuf;
|
||||
clen = pbuf_clen(pcur);
|
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
|
||||
pbufs_freed += clen;
|
||||
pbuf_free(pcur);
|
||||
}
|
||||
|
||||
/* Then, unchain the struct ip6_reassdata from the list and free it. */
|
||||
if (ipr == reassdatagrams) {
|
||||
reassdatagrams = ipr->next;
|
||||
} else {
|
||||
prev = reassdatagrams;
|
||||
while (prev != NULL) {
|
||||
if (prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
prev = prev->next;
|
||||
}
|
||||
if (prev != NULL) {
|
||||
prev->next = ipr->next;
|
||||
}
|
||||
}
|
||||
memp_free(MEMP_IP6_REASSDATA, ipr);
|
||||
|
||||
/* Finally, update number of pbufs in reassembly queue */
|
||||
LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
|
||||
ip6_reass_pbufcount -= pbufs_freed;
|
||||
}
|
||||
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
/**
|
||||
* Free the oldest datagram to make room for enqueueing new fragments.
|
||||
* The datagram ipr is not freed!
|
||||
*
|
||||
* @param ipr ip6_reassdata for the current fragment
|
||||
* @param pbufs_needed number of pbufs needed to enqueue
|
||||
* (used for freeing other datagrams if not enough space)
|
||||
*/
|
||||
static void
|
||||
ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
|
||||
{
|
||||
struct ip6_reassdata *r, *oldest;
|
||||
|
||||
/* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
|
||||
* but don't free the current datagram! */
|
||||
do {
|
||||
r = oldest = reassdatagrams;
|
||||
while (r != NULL) {
|
||||
if (r != ipr) {
|
||||
if (r->timer <= oldest->timer) {
|
||||
/* older than the previous oldest */
|
||||
oldest = r;
|
||||
}
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (oldest == ipr) {
|
||||
/* nothing to free, ipr is the only element on the list */
|
||||
return;
|
||||
}
|
||||
if (oldest != NULL) {
|
||||
ip6_reass_free_complete_datagram(oldest);
|
||||
}
|
||||
} while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
|
||||
}
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
/**
|
||||
* Reassembles incoming IPv6 fragments into an IPv6 datagram.
|
||||
*
|
||||
* @param p points to the IPv6 Fragment Header
|
||||
* @return NULL if reassembly is incomplete, pbuf pointing to
|
||||
* IPv6 Header if reassembly is complete
|
||||
*/
|
||||
struct pbuf *
|
||||
ip6_reass(struct pbuf *p)
|
||||
{
|
||||
struct ip6_reassdata *ipr, *ipr_prev;
|
||||
struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
|
||||
struct ip6_frag_hdr * frag_hdr;
|
||||
u16_t offset, len;
|
||||
u8_t clen, valid = 1;
|
||||
struct pbuf *q;
|
||||
|
||||
IP6_FRAG_STATS_INC(ip6_frag.recv);
|
||||
|
||||
if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) {
|
||||
/* ip6_frag_hdr must be in the first pbuf, not chained */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
|
||||
frag_hdr = (struct ip6_frag_hdr *) p->payload;
|
||||
|
||||
clen = pbuf_clen(p);
|
||||
|
||||
offset = ntohs(frag_hdr->_fragment_offset);
|
||||
|
||||
/* Calculate fragment length from IPv6 payload length.
|
||||
* Adjust for headers before Fragment Header.
|
||||
* And finally adjust by Fragment Header length. */
|
||||
len = ntohs(ip6_current_header()->_plen);
|
||||
len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN);
|
||||
len -= IP6_FRAG_HLEN;
|
||||
|
||||
/* Look for the datagram the fragment belongs to in the current datagram queue,
|
||||
* remembering the previous in the queue for later dequeueing. */
|
||||
for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
|
||||
/* Check if the incoming fragment matches the one currently present
|
||||
in the reassembly buffer. If so, we proceed with copying the
|
||||
fragment into the buffer. */
|
||||
if ((frag_hdr->_identification == ipr->identification) &&
|
||||
ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) &&
|
||||
ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) {
|
||||
IP6_FRAG_STATS_INC(ip6_frag.cachehit);
|
||||
break;
|
||||
}
|
||||
ipr_prev = ipr;
|
||||
}
|
||||
|
||||
if (ipr == NULL) {
|
||||
/* Enqueue a new datagram into the datagram queue */
|
||||
ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
|
||||
if (ipr == NULL) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
/* Make room and try again. */
|
||||
ip6_reass_remove_oldest_datagram(ipr, clen);
|
||||
ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
|
||||
if (ipr != NULL) {
|
||||
/* re-search ipr_prev since it might have been removed */
|
||||
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
|
||||
if (ipr_prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
|
||||
memset(ipr, 0, sizeof(struct ip6_reassdata));
|
||||
ipr->timer = IP_REASS_MAXAGE;
|
||||
|
||||
/* enqueue the new structure to the front of the list */
|
||||
ipr->next = reassdatagrams;
|
||||
reassdatagrams = ipr;
|
||||
|
||||
/* Use the current IPv6 header for src/dest address reference.
|
||||
* Eventually, we will replace it when we get the first fragment
|
||||
* (it might be this one, in any case, it is done later). */
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
|
||||
#else /* IPV6_FRAG_COPYHEADER */
|
||||
/* need to use the none-const pointer here: */
|
||||
ipr->iphdr = ip_data.current_ip6_header;
|
||||
#endif /* IPV6_FRAG_COPYHEADER */
|
||||
|
||||
/* copy the fragmented packet id. */
|
||||
ipr->identification = frag_hdr->_identification;
|
||||
|
||||
/* copy the nexth field */
|
||||
ipr->nexth = frag_hdr->_nexth;
|
||||
}
|
||||
|
||||
/* Check if we are allowed to enqueue more datagrams. */
|
||||
if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
ip6_reass_remove_oldest_datagram(ipr, clen);
|
||||
if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
|
||||
/* re-search ipr_prev since it might have been removed */
|
||||
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
|
||||
if (ipr_prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
/* @todo: send ICMPv6 time exceeded here? */
|
||||
/* drop this pbuf */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overwrite Fragment Header with our own helper struct. */
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (IPV6_FRAG_REQROOM > 0) {
|
||||
/* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
|
||||
This cannot fail since we already checked when receiving this fragment. */
|
||||
err_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
|
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == ERR_OK);
|
||||
}
|
||||
#else /* IPV6_FRAG_COPYHEADER */
|
||||
LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
|
||||
sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
|
||||
#endif /* IPV6_FRAG_COPYHEADER */
|
||||
iprh = (struct ip6_reass_helper *)p->payload;
|
||||
iprh->next_pbuf = NULL;
|
||||
iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
|
||||
iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
|
||||
|
||||
/* find the right place to insert this pbuf */
|
||||
/* Iterate through until we either get to the end of the list (append),
|
||||
* or we find on with a larger offset (insert). */
|
||||
for (q = ipr->p; q != NULL;) {
|
||||
iprh_tmp = (struct ip6_reass_helper*)q->payload;
|
||||
if (iprh->start < iprh_tmp->start) {
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
if (iprh->end > iprh_tmp->start) {
|
||||
/* fragment overlaps with following, throw away */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
if (iprh_prev != NULL) {
|
||||
if (iprh->start < iprh_prev->end) {
|
||||
/* fragment overlaps with previous, throw away */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
/* the new pbuf should be inserted before this */
|
||||
iprh->next_pbuf = q;
|
||||
if (iprh_prev != NULL) {
|
||||
/* not the fragment with the lowest offset */
|
||||
iprh_prev->next_pbuf = p;
|
||||
} else {
|
||||
/* fragment with the lowest offset */
|
||||
ipr->p = p;
|
||||
}
|
||||
break;
|
||||
} else if (iprh->start == iprh_tmp->start) {
|
||||
/* received the same datagram twice: no need to keep the datagram */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
} else if (iprh->start < iprh_tmp->end) {
|
||||
/* overlap: no need to keep the new datagram */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
} else {
|
||||
/* Check if the fragments received so far have no gaps. */
|
||||
if (iprh_prev != NULL) {
|
||||
if (iprh_prev->end != iprh_tmp->start) {
|
||||
/* There is a fragment missing between the current
|
||||
* and the previous fragment */
|
||||
valid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
q = iprh_tmp->next_pbuf;
|
||||
iprh_prev = iprh_tmp;
|
||||
}
|
||||
|
||||
/* If q is NULL, then we made it to the end of the list. Determine what to do now */
|
||||
if (q == NULL) {
|
||||
if (iprh_prev != NULL) {
|
||||
/* this is (for now), the fragment with the highest offset:
|
||||
* chain it to the last fragment */
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
iprh_prev->next_pbuf = p;
|
||||
if (iprh_prev->end != iprh->start) {
|
||||
valid = 0;
|
||||
}
|
||||
} else {
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
LWIP_ASSERT("no previous fragment, this must be the first fragment!",
|
||||
ipr->p == NULL);
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
/* this is the first fragment we ever received for this ip datagram */
|
||||
ipr->p = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Track the current number of pbufs current 'in-flight', in order to limit
|
||||
the number of fragments that may be enqueued at any one time */
|
||||
ip6_reass_pbufcount += clen;
|
||||
|
||||
/* Remember IPv6 header if this is the first fragment. */
|
||||
if (iprh->start == 0) {
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (iprh->next_pbuf != NULL) {
|
||||
MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
|
||||
}
|
||||
#else /* IPV6_FRAG_COPYHEADER */
|
||||
/* need to use the none-const pointer here: */
|
||||
ipr->iphdr = ip_data.current_ip6_header;
|
||||
#endif /* IPV6_FRAG_COPYHEADER */
|
||||
}
|
||||
|
||||
/* If this is the last fragment, calculate total packet length. */
|
||||
if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
|
||||
ipr->datagram_len = iprh->end;
|
||||
}
|
||||
|
||||
/* Additional validity tests: we have received first and last fragment. */
|
||||
iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
|
||||
if (iprh_tmp->start != 0) {
|
||||
valid = 0;
|
||||
}
|
||||
if (ipr->datagram_len == 0) {
|
||||
valid = 0;
|
||||
}
|
||||
|
||||
/* Final validity test: no gaps between current and last fragment. */
|
||||
iprh_prev = iprh;
|
||||
q = iprh->next_pbuf;
|
||||
while ((q != NULL) && valid) {
|
||||
iprh = (struct ip6_reass_helper*)q->payload;
|
||||
if (iprh_prev->end != iprh->start) {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
iprh_prev = iprh;
|
||||
q = iprh->next_pbuf;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
/* All fragments have been received */
|
||||
struct ip6_hdr* iphdr_ptr;
|
||||
|
||||
/* chain together the pbufs contained within the ip6_reassdata list. */
|
||||
iprh = (struct ip6_reass_helper*) ipr->p->payload;
|
||||
while (iprh != NULL) {
|
||||
struct pbuf* next_pbuf = iprh->next_pbuf;
|
||||
if (next_pbuf != NULL) {
|
||||
/* Save next helper struct (will be hidden in next step). */
|
||||
iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
|
||||
|
||||
/* hide the fragment header for every succeeding fragment */
|
||||
pbuf_header(next_pbuf, -IP6_FRAG_HLEN);
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (IPV6_FRAG_REQROOM > 0) {
|
||||
/* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
|
||||
err_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
|
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == ERR_OK);
|
||||
}
|
||||
#endif
|
||||
pbuf_cat(ipr->p, next_pbuf);
|
||||
}
|
||||
else {
|
||||
iprh_tmp = NULL;
|
||||
}
|
||||
|
||||
iprh = iprh_tmp;
|
||||
}
|
||||
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (IPV6_FRAG_REQROOM > 0) {
|
||||
/* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
|
||||
err_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
|
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == ERR_OK);
|
||||
}
|
||||
iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN);
|
||||
MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN);
|
||||
#else
|
||||
iphdr_ptr = ipr->iphdr;
|
||||
#endif
|
||||
|
||||
/* Adjust datagram length by adding header lengths. */
|
||||
ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr)
|
||||
+ IP6_FRAG_HLEN
|
||||
- IP6_HLEN);
|
||||
|
||||
/* Set payload length in ip header. */
|
||||
iphdr_ptr->_plen = htons(ipr->datagram_len);
|
||||
|
||||
/* Get the first pbuf. */
|
||||
p = ipr->p;
|
||||
|
||||
/* Restore Fragment Header in first pbuf. Mark as "single fragment"
|
||||
* packet. Restore nexth. */
|
||||
frag_hdr = (struct ip6_frag_hdr *) p->payload;
|
||||
frag_hdr->_nexth = ipr->nexth;
|
||||
frag_hdr->reserved = 0;
|
||||
frag_hdr->_fragment_offset = 0;
|
||||
frag_hdr->_identification = 0;
|
||||
|
||||
/* release the sources allocate for the fragment queue entry */
|
||||
if (reassdatagrams == ipr) {
|
||||
/* it was the first in the list */
|
||||
reassdatagrams = ipr->next;
|
||||
} else {
|
||||
/* it wasn't the first, so it must have a valid 'prev' */
|
||||
LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
|
||||
ipr_prev->next = ipr->next;
|
||||
}
|
||||
memp_free(MEMP_IP6_REASSDATA, ipr);
|
||||
|
||||
/* adjust the number of pbufs currently queued for reassembly. */
|
||||
ip6_reass_pbufcount -= pbuf_clen(p);
|
||||
|
||||
/* Move pbuf back to IPv6 header.
|
||||
This cannot fail since we already checked when receiving this fragment. */
|
||||
if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
|
||||
LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return the pbuf chain */
|
||||
return p;
|
||||
}
|
||||
/* the datagram is not (yet?) reassembled completely */
|
||||
return NULL;
|
||||
|
||||
nullreturn:
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_FRAG
|
||||
|
||||
/** Allocate a new struct pbuf_custom_ref */
|
||||
static struct pbuf_custom_ref*
|
||||
ip6_frag_alloc_pbuf_custom_ref(void)
|
||||
{
|
||||
return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
|
||||
}
|
||||
|
||||
/** Free a struct pbuf_custom_ref */
|
||||
static void
|
||||
ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
|
||||
{
|
||||
LWIP_ASSERT("p != NULL", p != NULL);
|
||||
memp_free(MEMP_FRAG_PBUF, p);
|
||||
}
|
||||
|
||||
/** Free-callback function to free a 'struct pbuf_custom_ref', called by
|
||||
* pbuf_free. */
|
||||
static void
|
||||
ip6_frag_free_pbuf_custom(struct pbuf *p)
|
||||
{
|
||||
struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
|
||||
LWIP_ASSERT("pcr != NULL", pcr != NULL);
|
||||
LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
|
||||
if (pcr->original != NULL) {
|
||||
pbuf_free(pcr->original);
|
||||
}
|
||||
ip6_frag_free_pbuf_custom_ref(pcr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment an IPv6 datagram if too large for the netif or path MTU.
|
||||
*
|
||||
* Chop the datagram in MTU sized chunks and send them in order
|
||||
* by pointing PBUF_REFs into p
|
||||
*
|
||||
* @param p ipv6 packet to send
|
||||
* @param netif the netif on which to send
|
||||
* @param dest destination ipv6 address to which to send
|
||||
*
|
||||
* @return ERR_OK if sent successfully, err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
|
||||
{
|
||||
struct ip6_hdr *original_ip6hdr;
|
||||
struct ip6_hdr *ip6hdr;
|
||||
struct ip6_frag_hdr * frag_hdr;
|
||||
struct pbuf *rambuf;
|
||||
struct pbuf *newpbuf;
|
||||
static u32_t identification;
|
||||
u16_t nfb;
|
||||
u16_t left, cop;
|
||||
u16_t mtu;
|
||||
u16_t fragment_offset = 0;
|
||||
u16_t last;
|
||||
u16_t poff = IP6_HLEN;
|
||||
u16_t newpbuflen = 0;
|
||||
u16_t left_to_copy;
|
||||
|
||||
identification++;
|
||||
|
||||
original_ip6hdr = (struct ip6_hdr *)p->payload;
|
||||
|
||||
mtu = nd6_get_destination_mtu(dest, netif);
|
||||
|
||||
/* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
|
||||
left = p->tot_len - IP6_HLEN;
|
||||
|
||||
nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
|
||||
|
||||
while (left) {
|
||||
last = (left <= nfb);
|
||||
|
||||
/* Fill this fragment */
|
||||
cop = last ? left : nfb;
|
||||
|
||||
/* When not using a static buffer, create a chain of pbufs.
|
||||
* The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
|
||||
* The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
|
||||
* but limited to the size of an mtu.
|
||||
*/
|
||||
rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
|
||||
if (rambuf == NULL) {
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
LWIP_ASSERT("this needs a pbuf in one piece!",
|
||||
(p->len >= (IP6_HLEN + IP6_FRAG_HLEN)));
|
||||
SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
|
||||
ip6hdr = (struct ip6_hdr *)rambuf->payload;
|
||||
frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
|
||||
|
||||
/* Can just adjust p directly for needed offset. */
|
||||
p->payload = (u8_t *)p->payload + poff;
|
||||
p->len -= poff;
|
||||
p->tot_len -= poff;
|
||||
|
||||
left_to_copy = cop;
|
||||
while (left_to_copy) {
|
||||
struct pbuf_custom_ref *pcr;
|
||||
newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
|
||||
/* Is this pbuf already empty? */
|
||||
if (!newpbuflen) {
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
pcr = ip6_frag_alloc_pbuf_custom_ref();
|
||||
if (pcr == NULL) {
|
||||
pbuf_free(rambuf);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
/* Mirror this pbuf, although we might not need all of it. */
|
||||
newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
|
||||
if (newpbuf == NULL) {
|
||||
ip6_frag_free_pbuf_custom_ref(pcr);
|
||||
pbuf_free(rambuf);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
pbuf_ref(p);
|
||||
pcr->original = p;
|
||||
pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
|
||||
|
||||
/* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
|
||||
* so that it is removed when pbuf_dechain is later called on rambuf.
|
||||
*/
|
||||
pbuf_cat(rambuf, newpbuf);
|
||||
left_to_copy -= newpbuflen;
|
||||
if (left_to_copy) {
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
poff = newpbuflen;
|
||||
|
||||
/* Set headers */
|
||||
frag_hdr->_nexth = original_ip6hdr->_nexth;
|
||||
frag_hdr->reserved = 0;
|
||||
frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
|
||||
frag_hdr->_identification = htonl(identification);
|
||||
|
||||
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
|
||||
IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
|
||||
|
||||
/* No need for separate header pbuf - we allowed room for it in rambuf
|
||||
* when allocated.
|
||||
*/
|
||||
IP6_FRAG_STATS_INC(ip6_frag.xmit);
|
||||
netif->output_ip6(netif, rambuf, dest);
|
||||
|
||||
/* Unfortunately we can't reuse rambuf - the hardware may still be
|
||||
* using the buffer. Instead we free it (and the ensuing chain) and
|
||||
* recreate it next time round the loop. If we're lucky the hardware
|
||||
* will have already sent the packet, the free will really free, and
|
||||
* there will be zero memory penalty.
|
||||
*/
|
||||
|
||||
pbuf_free(rambuf);
|
||||
left -= cop;
|
||||
fragment_offset += cop;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
|
||||
598
ext/lwip/src/core/ipv6/mld6.c
Normal file
598
ext/lwip/src/core/ipv6/mld6.c
Normal file
@@ -0,0 +1,598 @@
|
||||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. 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: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup mld6 MLD6
|
||||
* @ingroup ip6
|
||||
* Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
|
||||
* No support for MLDv2.\n
|
||||
* To be called from TCPIP thread
|
||||
*/
|
||||
|
||||
/* Based on igmp.c implementation of igmp v2 protocol */
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/icmp6.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
* MLD constants
|
||||
*/
|
||||
#define MLD6_HL 1
|
||||
#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500)
|
||||
|
||||
#define MLD6_GROUP_NON_MEMBER 0
|
||||
#define MLD6_GROUP_DELAYING_MEMBER 1
|
||||
#define MLD6_GROUP_IDLE_MEMBER 2
|
||||
|
||||
|
||||
/* The list of joined groups. */
|
||||
static struct mld_group* mld_group_list;
|
||||
|
||||
|
||||
/* Forward declarations. */
|
||||
static struct mld_group * mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
|
||||
static err_t mld6_free_group(struct mld_group *group);
|
||||
static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
|
||||
static void mld6_send(struct mld_group *group, u8_t type);
|
||||
|
||||
|
||||
/**
|
||||
* Stop MLD processing on interface
|
||||
*
|
||||
* @param netif network interface on which stop MLD processing
|
||||
*/
|
||||
err_t
|
||||
mld6_stop(struct netif *netif)
|
||||
{
|
||||
struct mld_group *group = mld_group_list;
|
||||
struct mld_group *prev = NULL;
|
||||
struct mld_group *next;
|
||||
|
||||
/* look for groups joined on this interface further down the list */
|
||||
while (group != NULL) {
|
||||
next = group->next;
|
||||
/* is it a group joined on this interface? */
|
||||
if (group->netif == netif) {
|
||||
/* is it the first group of the list? */
|
||||
if (group == mld_group_list) {
|
||||
mld_group_list = next;
|
||||
}
|
||||
/* is there a "previous" group defined? */
|
||||
if (prev != NULL) {
|
||||
prev->next = next;
|
||||
}
|
||||
/* disable the group at the MAC level */
|
||||
if (netif->mld_mac_filter != NULL) {
|
||||
netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER);
|
||||
}
|
||||
/* free group */
|
||||
memp_free(MEMP_MLD6_GROUP, group);
|
||||
} else {
|
||||
/* change the "previous" */
|
||||
prev = group;
|
||||
}
|
||||
/* move to "next" */
|
||||
group = next;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report MLD memberships for this interface
|
||||
*
|
||||
* @param netif network interface on which report MLD memberships
|
||||
*/
|
||||
void
|
||||
mld6_report_groups(struct netif *netif)
|
||||
{
|
||||
struct mld_group *group = mld_group_list;
|
||||
|
||||
while (group != NULL) {
|
||||
if (group->netif == netif) {
|
||||
mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a group that is joined on a netif
|
||||
*
|
||||
* @param ifp the network interface for which to look
|
||||
* @param addr the group ipv6 address to search for
|
||||
* @return a struct mld_group* if the group has been found,
|
||||
* NULL if the group wasn't found.
|
||||
*/
|
||||
struct mld_group *
|
||||
mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
|
||||
{
|
||||
struct mld_group *group = mld_group_list;
|
||||
|
||||
while (group != NULL) {
|
||||
if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) {
|
||||
return group;
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create a new group
|
||||
*
|
||||
* @param ifp the network interface for which to create
|
||||
* @param addr the new group ipv6
|
||||
* @return a struct mld_group*,
|
||||
* NULL on memory error.
|
||||
*/
|
||||
static struct mld_group *
|
||||
mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
|
||||
{
|
||||
struct mld_group *group;
|
||||
|
||||
group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
|
||||
if (group != NULL) {
|
||||
group->netif = ifp;
|
||||
ip6_addr_set(&(group->group_address), addr);
|
||||
group->timer = 0; /* Not running */
|
||||
group->group_state = MLD6_GROUP_IDLE_MEMBER;
|
||||
group->last_reporter_flag = 0;
|
||||
group->use = 0;
|
||||
group->next = mld_group_list;
|
||||
|
||||
mld_group_list = group;
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a group in the mld_group_list and free
|
||||
*
|
||||
* @param group the group to remove
|
||||
* @return ERR_OK if group was removed from the list, an err_t otherwise
|
||||
*/
|
||||
static err_t
|
||||
mld6_free_group(struct mld_group *group)
|
||||
{
|
||||
err_t err = ERR_OK;
|
||||
|
||||
/* Is it the first group? */
|
||||
if (mld_group_list == group) {
|
||||
mld_group_list = group->next;
|
||||
} else {
|
||||
/* look for group further down the list */
|
||||
struct mld_group *tmpGroup;
|
||||
for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
|
||||
if (tmpGroup->next == group) {
|
||||
tmpGroup->next = group->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Group not find group */
|
||||
if (tmpGroup == NULL) {
|
||||
err = ERR_ARG;
|
||||
}
|
||||
}
|
||||
/* free group */
|
||||
memp_free(MEMP_MLD6_GROUP, group);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process an input MLD message. Called by icmp6_input.
|
||||
*
|
||||
* @param p the mld packet, p->payload pointing to the icmpv6 header
|
||||
* @param inp the netif on which this packet was received
|
||||
*/
|
||||
void
|
||||
mld6_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
struct mld_header * mld_hdr;
|
||||
struct mld_group* group;
|
||||
|
||||
MLD6_STATS_INC(mld6.recv);
|
||||
|
||||
/* Check that mld header fits in packet. */
|
||||
if (p->len < sizeof(struct mld_header)) {
|
||||
/* @todo debug message */
|
||||
pbuf_free(p);
|
||||
MLD6_STATS_INC(mld6.lenerr);
|
||||
MLD6_STATS_INC(mld6.drop);
|
||||
return;
|
||||
}
|
||||
|
||||
mld_hdr = (struct mld_header *)p->payload;
|
||||
|
||||
switch (mld_hdr->type) {
|
||||
case ICMP6_TYPE_MLQ: /* Multicast listener query. */
|
||||
/* Is it a general query? */
|
||||
if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
|
||||
ip6_addr_isany(&(mld_hdr->multicast_address))) {
|
||||
MLD6_STATS_INC(mld6.rx_general);
|
||||
/* Report all groups, except all nodes group, and if-local groups. */
|
||||
group = mld_group_list;
|
||||
while (group != NULL) {
|
||||
if ((group->netif == inp) &&
|
||||
(!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
|
||||
(!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
|
||||
mld6_delayed_report(group, mld_hdr->max_resp_delay);
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
} else {
|
||||
/* Have we joined this group?
|
||||
* We use IP6 destination address to have a memory aligned copy.
|
||||
* mld_hdr->multicast_address should be the same. */
|
||||
MLD6_STATS_INC(mld6.rx_group);
|
||||
group = mld6_lookfor_group(inp, ip6_current_dest_addr());
|
||||
if (group != NULL) {
|
||||
/* Schedule a report. */
|
||||
mld6_delayed_report(group, mld_hdr->max_resp_delay);
|
||||
}
|
||||
}
|
||||
break; /* ICMP6_TYPE_MLQ */
|
||||
case ICMP6_TYPE_MLR: /* Multicast listener report. */
|
||||
/* Have we joined this group?
|
||||
* We use IP6 destination address to have a memory aligned copy.
|
||||
* mld_hdr->multicast_address should be the same. */
|
||||
MLD6_STATS_INC(mld6.rx_report);
|
||||
group = mld6_lookfor_group(inp, ip6_current_dest_addr());
|
||||
if (group != NULL) {
|
||||
/* If we are waiting to report, cancel it. */
|
||||
if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
|
||||
group->timer = 0; /* stopped */
|
||||
group->group_state = MLD6_GROUP_IDLE_MEMBER;
|
||||
group->last_reporter_flag = 0;
|
||||
}
|
||||
}
|
||||
break; /* ICMP6_TYPE_MLR */
|
||||
case ICMP6_TYPE_MLD: /* Multicast listener done. */
|
||||
/* Do nothing, router will query us. */
|
||||
break; /* ICMP6_TYPE_MLD */
|
||||
default:
|
||||
MLD6_STATS_INC(mld6.proterr);
|
||||
MLD6_STATS_INC(mld6.drop);
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Join a group on a network interface.
|
||||
*
|
||||
* @param srcaddr ipv6 address of the network interface which should
|
||||
* join a new group. If IP6_ADDR_ANY, join on all netifs
|
||||
* @param groupaddr the ipv6 address of the group to join
|
||||
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct netif *netif;
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we join this interface ? */
|
||||
if (ip6_addr_isany(srcaddr) ||
|
||||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
|
||||
err = mld6_joingroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Join a group on a network interface.
|
||||
*
|
||||
* @param netif the network interface which should join a new group.
|
||||
* @param groupaddr the ipv6 address of the group to join
|
||||
* @return ERR_OK if group was joined on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
struct mld_group *group;
|
||||
|
||||
/* find group or create a new one if not found */
|
||||
group = mld6_lookfor_group(netif, groupaddr);
|
||||
|
||||
if (group == NULL) {
|
||||
/* Joining a new group. Create a new group entry. */
|
||||
group = mld6_new_group(netif, groupaddr);
|
||||
if (group == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Activate this address on the MAC layer. */
|
||||
if (netif->mld_mac_filter != NULL) {
|
||||
netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER);
|
||||
}
|
||||
|
||||
/* Report our membership. */
|
||||
MLD6_STATS_INC(mld6.tx_report);
|
||||
mld6_send(group, ICMP6_TYPE_MLR);
|
||||
mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
|
||||
}
|
||||
|
||||
/* Increment group use */
|
||||
group->use++;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Leave a group on a network interface.
|
||||
*
|
||||
* @param srcaddr ipv6 address of the network interface which should
|
||||
* leave the group. If IP6_ISANY, leave on all netifs
|
||||
* @param groupaddr the ipv6 address of the group to leave
|
||||
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct netif *netif;
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we leave this interface ? */
|
||||
if (ip6_addr_isany(srcaddr) ||
|
||||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
|
||||
err_t res = mld6_leavegroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
/* Store this result if we have not yet gotten a success */
|
||||
err = res;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Leave a group on a network interface.
|
||||
*
|
||||
* @param netif the network interface which should leave the group.
|
||||
* @param groupaddr the ipv6 address of the group to leave
|
||||
* @return ERR_OK if group was left on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
struct mld_group *group;
|
||||
|
||||
/* find group */
|
||||
group = mld6_lookfor_group(netif, groupaddr);
|
||||
|
||||
if (group != NULL) {
|
||||
/* Leave if there is no other use of the group */
|
||||
if (group->use <= 1) {
|
||||
/* If we are the last reporter for this group */
|
||||
if (group->last_reporter_flag) {
|
||||
MLD6_STATS_INC(mld6.tx_leave);
|
||||
mld6_send(group, ICMP6_TYPE_MLD);
|
||||
}
|
||||
|
||||
/* Disable the group at the MAC level */
|
||||
if (netif->mld_mac_filter != NULL) {
|
||||
netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER);
|
||||
}
|
||||
|
||||
/* Free the group */
|
||||
mld6_free_group(group);
|
||||
} else {
|
||||
/* Decrement group use */
|
||||
group->use--;
|
||||
}
|
||||
|
||||
/* Left group */
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* Group not found */
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Periodic timer for mld processing. Must be called every
|
||||
* MLD6_TMR_INTERVAL milliseconds (100).
|
||||
*
|
||||
* When a delaying member expires, a membership report is sent.
|
||||
*/
|
||||
void
|
||||
mld6_tmr(void)
|
||||
{
|
||||
struct mld_group *group = mld_group_list;
|
||||
|
||||
while (group != NULL) {
|
||||
if (group->timer > 0) {
|
||||
group->timer--;
|
||||
if (group->timer == 0) {
|
||||
/* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
|
||||
if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
|
||||
MLD6_STATS_INC(mld6.tx_report);
|
||||
mld6_send(group, ICMP6_TYPE_MLR);
|
||||
group->group_state = MLD6_GROUP_IDLE_MEMBER;
|
||||
}
|
||||
}
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a delayed membership report for a group
|
||||
*
|
||||
* @param group the mld_group for which "delaying" membership report
|
||||
* should be sent
|
||||
* @param maxresp the max resp delay provided in the query
|
||||
*/
|
||||
static void
|
||||
mld6_delayed_report(struct mld_group *group, u16_t maxresp)
|
||||
{
|
||||
/* Convert maxresp from milliseconds to tmr ticks */
|
||||
maxresp = maxresp / MLD6_TMR_INTERVAL;
|
||||
if (maxresp == 0) {
|
||||
maxresp = 1;
|
||||
}
|
||||
|
||||
#ifdef LWIP_RAND
|
||||
/* Randomize maxresp. (if LWIP_RAND is supported) */
|
||||
maxresp = LWIP_RAND() % maxresp;
|
||||
if (maxresp == 0) {
|
||||
maxresp = 1;
|
||||
}
|
||||
#endif /* LWIP_RAND */
|
||||
|
||||
/* Apply timer value if no report has been scheduled already. */
|
||||
if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
|
||||
((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
|
||||
((group->timer == 0) || (maxresp < group->timer)))) {
|
||||
group->timer = maxresp;
|
||||
group->group_state = MLD6_GROUP_DELAYING_MEMBER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a MLD message (report or done).
|
||||
*
|
||||
* An IPv6 hop-by-hop options header with a router alert option
|
||||
* is prepended.
|
||||
*
|
||||
* @param group the group to report or quit
|
||||
* @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
|
||||
*/
|
||||
static void
|
||||
mld6_send(struct mld_group *group, u8_t type)
|
||||
{
|
||||
struct mld_header * mld_hdr;
|
||||
struct pbuf * p;
|
||||
const ip6_addr_t * src_addr;
|
||||
|
||||
/* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
|
||||
p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
|
||||
if (p == NULL) {
|
||||
MLD6_STATS_INC(mld6.memerr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move to make room for Hop-by-hop options header. */
|
||||
if (pbuf_header(p, -IP6_HBH_HLEN)) {
|
||||
pbuf_free(p);
|
||||
MLD6_STATS_INC(mld6.lenerr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Select our source address. */
|
||||
if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) {
|
||||
/* This is a special case, when we are performing duplicate address detection.
|
||||
* We must join the multicast group, but we don't have a valid address yet. */
|
||||
src_addr = IP6_ADDR_ANY6;
|
||||
} else {
|
||||
/* Use link-local address as source address. */
|
||||
src_addr = netif_ip6_addr(group->netif, 0);
|
||||
}
|
||||
|
||||
/* MLD message header pointer. */
|
||||
mld_hdr = (struct mld_header *)p->payload;
|
||||
|
||||
/* Set fields. */
|
||||
mld_hdr->type = type;
|
||||
mld_hdr->code = 0;
|
||||
mld_hdr->chksum = 0;
|
||||
mld_hdr->max_resp_delay = 0;
|
||||
mld_hdr->reserved = 0;
|
||||
ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));
|
||||
|
||||
#if CHECKSUM_GEN_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(group->netif, NETIF_CHECKSUM_GEN_ICMP6) {
|
||||
mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
|
||||
src_addr, &(group->group_address));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_ICMP6 */
|
||||
|
||||
/* Add hop-by-hop headers options: router alert with MLD value. */
|
||||
ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
|
||||
|
||||
/* Send the packet out. */
|
||||
MLD6_STATS_INC(mld6.xmit);
|
||||
ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
|
||||
MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 */
|
||||
1884
ext/lwip/src/core/ipv6/nd6.c
Normal file
1884
ext/lwip/src/core/ipv6/nd6.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -54,9 +54,6 @@
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/sys.h"
|
||||
@@ -65,8 +62,93 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if MEM_USE_POOLS
|
||||
/* lwIP head implemented with different sized pools */
|
||||
#if MEM_LIBC_MALLOC || MEM_USE_POOLS
|
||||
/** mem_init is not used when using pools instead of a heap or using
|
||||
* C library malloc().
|
||||
*/
|
||||
void
|
||||
mem_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
/** mem_trim is not used when using pools instead of a heap or using
|
||||
* C library malloc(): we can't free part of a pool element and the stack
|
||||
* support mem_trim() to return a different pointer
|
||||
*/
|
||||
void*
|
||||
mem_trim(void *mem, mem_size_t size)
|
||||
{
|
||||
LWIP_UNUSED_ARG(size);
|
||||
return mem;
|
||||
}
|
||||
#endif /* MEM_LIBC_MALLOC || MEM_USE_POOLS */
|
||||
|
||||
#if MEM_LIBC_MALLOC
|
||||
/* lwIP heap implemented using C library malloc() */
|
||||
|
||||
/* in case C library malloc() needs extra protection,
|
||||
* allow these defines to be overridden.
|
||||
*/
|
||||
#ifndef mem_clib_free
|
||||
#define mem_clib_free free
|
||||
#endif
|
||||
#ifndef mem_clib_malloc
|
||||
#define mem_clib_malloc malloc
|
||||
#endif
|
||||
#ifndef mem_clib_calloc
|
||||
#define mem_clib_calloc calloc
|
||||
#endif
|
||||
|
||||
#if LWIP_STATS && MEM_STATS
|
||||
#define MEM_LIBC_STATSHELPER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(mem_size_t))
|
||||
#else
|
||||
#define MEM_LIBC_STATSHELPER_SIZE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Allocate a block of memory with a minimum of 'size' bytes.
|
||||
*
|
||||
* @param size is the minimum size of the requested block in bytes.
|
||||
* @return pointer to allocated memory or NULL if no free memory was found.
|
||||
*
|
||||
* Note that the returned value must always be aligned (as defined by MEM_ALIGNMENT).
|
||||
*/
|
||||
void *
|
||||
mem_malloc(mem_size_t size)
|
||||
{
|
||||
void* ret = mem_clib_malloc(size + MEM_LIBC_STATSHELPER_SIZE);
|
||||
if (ret == NULL) {
|
||||
MEM_STATS_INC(err);
|
||||
} else {
|
||||
LWIP_ASSERT("malloc() must return aligned memory", LWIP_MEM_ALIGN(ret) == ret);
|
||||
#if LWIP_STATS && MEM_STATS
|
||||
*(mem_size_t*)ret = size;
|
||||
ret = (u8_t*)ret + MEM_LIBC_STATSHELPER_SIZE;
|
||||
MEM_STATS_INC_USED(used, size);
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Put memory back on the heap
|
||||
*
|
||||
* @param rmem is the pointer as returned by a previous call to mem_malloc()
|
||||
*/
|
||||
void
|
||||
mem_free(void *rmem)
|
||||
{
|
||||
LWIP_ASSERT("rmem != NULL", (rmem != NULL));
|
||||
LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
|
||||
#if LWIP_STATS && MEM_STATS
|
||||
rmem = (u8_t*)rmem - MEM_LIBC_STATSHELPER_SIZE;
|
||||
MEM_STATS_DEC_USED(used, *(mem_size_t*)rmem);
|
||||
#endif
|
||||
mem_clib_free(rmem);
|
||||
}
|
||||
|
||||
#elif MEM_USE_POOLS
|
||||
|
||||
/* lwIP heap implemented with different sized pools */
|
||||
|
||||
/**
|
||||
* Allocate memory: determine the smallest pool that is big enough
|
||||
@@ -89,12 +171,13 @@ again:
|
||||
#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
|
||||
/* is this pool big enough to hold an element of the required size
|
||||
plus a struct memp_malloc_helper that saves the pool this element came from? */
|
||||
if (required_size <= memp_sizes[poolnr]) {
|
||||
if (required_size <= memp_pools[poolnr]->size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (poolnr > MEMP_POOL_LAST) {
|
||||
LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
|
||||
MEM_STATS_INC(err);
|
||||
return NULL;
|
||||
}
|
||||
element = (struct memp_malloc_helper*)memp_malloc(poolnr);
|
||||
@@ -108,6 +191,7 @@ again:
|
||||
goto again;
|
||||
}
|
||||
#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
|
||||
MEM_STATS_INC(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -116,6 +200,15 @@ again:
|
||||
/* and return a pointer to the memory directly after the struct memp_malloc_helper */
|
||||
ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
|
||||
/* truncating to u16_t is safe because struct memp_desc::size is u16_t */
|
||||
element->size = (u16_t)size;
|
||||
MEM_STATS_INC_USED(used, element->size);
|
||||
#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
/* initialize unused memory (diff between requested size and selected pool's size) */
|
||||
memset((u8_t*)ret + size, 0xcd, memp_pools[poolnr]->size - size);
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -135,12 +228,27 @@ mem_free(void *rmem)
|
||||
LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
|
||||
|
||||
/* get the original struct memp_malloc_helper */
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
|
||||
|
||||
LWIP_ASSERT("hmem != NULL", (hmem != NULL));
|
||||
LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
|
||||
LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
|
||||
|
||||
MEM_STATS_DEC_USED(used, hmem->size);
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
{
|
||||
u16_t i;
|
||||
LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size",
|
||||
hmem->size <= memp_pools[hmem->poolnr]->size);
|
||||
/* check that unused memory remained untouched (diff between requested size and selected pool's size) */
|
||||
for (i = hmem->size; i < memp_pools[hmem->poolnr]->size; i++) {
|
||||
u8_t data = *((u8_t*)rmem + i);
|
||||
LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd);
|
||||
}
|
||||
}
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
|
||||
/* and put it in the pool we saved earlier */
|
||||
memp_free(hmem->poolnr, hmem);
|
||||
}
|
||||
@@ -151,7 +259,7 @@ mem_free(void *rmem)
|
||||
/**
|
||||
* The heap is made up as a list of structs of this type.
|
||||
* This does not have to be aligned since for getting its size,
|
||||
* we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes.
|
||||
* we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
|
||||
*/
|
||||
struct mem {
|
||||
/** index (-> ram[next]) of the next struct */
|
||||
@@ -179,7 +287,7 @@ struct mem {
|
||||
* how that space is calculated). */
|
||||
#ifndef LWIP_RAM_HEAP_POINTER
|
||||
/** the heap. we need one struct mem at the end and some room for alignment */
|
||||
u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];
|
||||
LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U*SIZEOF_STRUCT_MEM));
|
||||
#define LWIP_RAM_HEAP_POINTER ram_heap
|
||||
#endif /* LWIP_RAM_HEAP_POINTER */
|
||||
|
||||
@@ -334,6 +442,7 @@ mem_free(void *rmem)
|
||||
/* protect the heap from concurrent access */
|
||||
LWIP_MEM_FREE_PROTECT();
|
||||
/* Get the corresponding struct mem ... */
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
|
||||
/* ... which has to be in a used state ... */
|
||||
LWIP_ASSERT("mem_free: mem->used", mem->used);
|
||||
@@ -400,6 +509,7 @@ mem_trim(void *rmem, mem_size_t newsize)
|
||||
return rmem;
|
||||
}
|
||||
/* Get the corresponding struct mem ... */
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
|
||||
/* ... and its offset pointer */
|
||||
ptr = (mem_size_t)((u8_t *)mem - ram);
|
||||
@@ -482,7 +592,6 @@ mem_trim(void *rmem, mem_size_t newsize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adam's mem_malloc() plus solution for bug #17922
|
||||
* Allocate a block of memory with a minimum of 'size' bytes.
|
||||
*
|
||||
* @param size is the minimum size of the requested block in bytes.
|
||||
@@ -490,8 +599,6 @@ mem_trim(void *rmem, mem_size_t newsize)
|
||||
*
|
||||
* Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
void *
|
||||
mem_malloc(mem_size_t size)
|
||||
{
|
||||
@@ -531,26 +638,15 @@ mem_malloc(mem_size_t size)
|
||||
/* Scan through the heap searching for a free block that is big enough,
|
||||
* beginning with the lowest free block.
|
||||
*/
|
||||
printf("ptr = (mem_size_t)((u8_t *)lfree - ram) = %d\n", (mem_size_t)((u8_t *)lfree - ram));
|
||||
printf("MEM_SIZE_ALIGNED - size = %d\n", MEM_SIZE_ALIGNED - size);
|
||||
//printf("\n", );
|
||||
|
||||
for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
|
||||
ptr = ((struct mem *)(void *)&ram[ptr])->next) {
|
||||
|
||||
printf("ptr = 0x%x\n", ptr);
|
||||
mem = (struct mem *)(void *)&ram[ptr];
|
||||
printf("mem = 0x%x\n", mem);
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
mem_free_count = 0;
|
||||
printf("A\n");
|
||||
LWIP_MEM_ALLOC_UNPROTECT();
|
||||
/* allow mem_free or mem_trim to run */
|
||||
printf("B\n");
|
||||
LWIP_MEM_ALLOC_PROTECT();
|
||||
printf("C\n");
|
||||
if (mem_free_count != 0) {
|
||||
printf("mem_free_count != 0\n");
|
||||
/* If mem_free or mem_trim have run, we have to restart since they
|
||||
could have altered our current struct mem. */
|
||||
local_mem_free_count = 1;
|
||||
@@ -558,9 +654,6 @@ printf("ptr = 0x%x\n", ptr);
|
||||
}
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
|
||||
if(mem == NULL)
|
||||
printf("mem == NULL\n");
|
||||
|
||||
if ((!mem->used) &&
|
||||
(mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
|
||||
/* mem is not used and at least perfect fit is possible:
|
||||
@@ -595,7 +688,7 @@ if(mem == NULL)
|
||||
/* (a mem2 struct does no fit into the user data space of mem and mem->next will always
|
||||
* be used at this point: if not we have 2 unused structs in a row, plug_holes should have
|
||||
* take care of this).
|
||||
* -> near fit or excact fit: do not split, no mem2 creation
|
||||
* -> near fit or exact fit: do not split, no mem2 creation
|
||||
* also can't move mem->next directly behind mem, since mem->next
|
||||
* will always be used at this point!
|
||||
*/
|
||||
@@ -649,6 +742,15 @@ mem_malloc_adjust_lfree:
|
||||
}
|
||||
|
||||
#endif /* MEM_USE_POOLS */
|
||||
|
||||
#if MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS)
|
||||
void *
|
||||
mem_calloc(mem_size_t count, mem_size_t size)
|
||||
{
|
||||
return mem_clib_calloc(count, size);
|
||||
}
|
||||
|
||||
#else /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
|
||||
/**
|
||||
* Contiguously allocates enough space for count objects that are size bytes
|
||||
* of memory each and returns a pointer to the allocated memory.
|
||||
@@ -659,7 +761,8 @@ mem_malloc_adjust_lfree:
|
||||
* @param size size of the objects to allocate
|
||||
* @return pointer to allocated memory / NULL pointer if there is an error
|
||||
*/
|
||||
void *mem_calloc(mem_size_t count, mem_size_t size)
|
||||
void *
|
||||
mem_calloc(mem_size_t count, mem_size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
@@ -667,9 +770,8 @@ void *mem_calloc(mem_size_t count, mem_size_t size)
|
||||
p = mem_malloc(count * size);
|
||||
if (p) {
|
||||
/* zero the memory */
|
||||
memset(p, 0, count * size);
|
||||
memset(p, 0, (size_t)count * (size_t)size);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif /* !MEM_LIBC_MALLOC */
|
||||
#endif /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
|
||||
501
ext/lwip/src/core/memp.c
Normal file
501
ext/lwip/src/core/memp.c
Normal file
@@ -0,0 +1,501 @@
|
||||
/**
|
||||
* @file
|
||||
* Dynamic pool memory manager
|
||||
*
|
||||
* lwIP has dedicated pools for many structures (netconn, protocol control blocks,
|
||||
* packet buffers, ...). All these pools are managed here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup mempool Memory pools
|
||||
* @ingroup infrastructure
|
||||
* Custom memory pools
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Make sure we include everything we need for size calculation required by memp_std.h */
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/ip4_frag.h"
|
||||
#include "lwip/netbuf.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
#include "lwip/priv/api_msg.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netifapi.h"
|
||||
#include "lwip/etharp.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/timeouts.h"
|
||||
/* needed by default MEMP_NUM_SYS_TIMEOUT */
|
||||
#include "netif/ppp/ppp_opts.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/ip6_frag.h"
|
||||
#include "lwip/mld6.h"
|
||||
|
||||
|
||||
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
|
||||
#include "lwip/priv/memp_std.h"
|
||||
|
||||
const struct memp_desc* const memp_pools[MEMP_MAX] = {
|
||||
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
|
||||
#include "lwip/priv/memp_std.h"
|
||||
};
|
||||
|
||||
#if MEMP_MEM_MALLOC && MEMP_OVERFLOW_CHECK >= 2
|
||||
#undef MEMP_OVERFLOW_CHECK
|
||||
/* MEMP_OVERFLOW_CHECK >= 2 does not work with MEMP_MEM_MALLOC, use 1 instead */
|
||||
#define MEMP_OVERFLOW_CHECK 1
|
||||
#endif
|
||||
|
||||
#if MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC
|
||||
/**
|
||||
* Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
|
||||
*/
|
||||
static int
|
||||
memp_sanity(const struct memp_desc *desc)
|
||||
{
|
||||
struct memp *t, *h;
|
||||
|
||||
t = *desc->tab;
|
||||
if (t != NULL) {
|
||||
for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
|
||||
h = ((h->next != NULL) ? h->next->next : NULL)) {
|
||||
if (t == h) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC */
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
/**
|
||||
* Check if a memp element was victim of an overflow
|
||||
* (e.g. the restricted area after it has been altered)
|
||||
*
|
||||
* @param p the memp element to check
|
||||
* @param memp_type the pool p comes from
|
||||
*/
|
||||
static void
|
||||
memp_overflow_check_element_overflow(struct memp *p, const struct memp_desc *desc)
|
||||
{
|
||||
u16_t k;
|
||||
u8_t *m;
|
||||
#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
|
||||
m = (u8_t*)p + MEMP_SIZE + desc->size;
|
||||
for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
|
||||
if (m[k] != 0xcd) {
|
||||
char errstr[128] = "detected memp overflow in pool ";
|
||||
strcat(errstr, desc->desc);
|
||||
LWIP_ASSERT(errstr, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a memp element was victim of an underflow
|
||||
* (e.g. the restricted area before it has been altered)
|
||||
*
|
||||
* @param p the memp element to check
|
||||
* @param memp_type the pool p comes from
|
||||
*/
|
||||
static void
|
||||
memp_overflow_check_element_underflow(struct memp *p, const struct memp_desc *desc)
|
||||
{
|
||||
u16_t k;
|
||||
u8_t *m;
|
||||
#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
|
||||
m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
|
||||
for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
|
||||
if (m[k] != 0xcd) {
|
||||
char errstr[128] = "detected memp underflow in pool ";
|
||||
strcat(errstr, desc->desc);
|
||||
LWIP_ASSERT(errstr, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the restricted area of on memp element.
|
||||
*/
|
||||
static void
|
||||
memp_overflow_init_element(struct memp *p, const struct memp_desc *desc)
|
||||
{
|
||||
u8_t *m;
|
||||
#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
|
||||
m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
|
||||
memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
|
||||
#endif
|
||||
#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
|
||||
m = (u8_t*)p + MEMP_SIZE + desc->size;
|
||||
memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK >= 2
|
||||
/**
|
||||
* Do an overflow check for all elements in every pool.
|
||||
*
|
||||
* @see memp_overflow_check_element for a description of the check
|
||||
*/
|
||||
static void
|
||||
memp_overflow_check_all(void)
|
||||
{
|
||||
u16_t i, j;
|
||||
struct memp *p;
|
||||
SYS_ARCH_DECL_PROTECT(old_level);
|
||||
SYS_ARCH_PROTECT(old_level);
|
||||
|
||||
for (i = 0; i < MEMP_MAX; ++i) {
|
||||
p = (struct memp *)(size_t)(memp_pools[i]->base);
|
||||
for (j = 0; j < memp_pools[i]->num; ++j) {
|
||||
memp_overflow_check_element_overflow(p, memp_pools[i]);
|
||||
memp_overflow_check_element_underflow(p, memp_pools[i]);
|
||||
p = (struct memp*)(size_t)((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED);
|
||||
}
|
||||
}
|
||||
SYS_ARCH_UNPROTECT(old_level);
|
||||
}
|
||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
||||
|
||||
#if !MEMP_MEM_MALLOC
|
||||
/**
|
||||
* Initialize the restricted areas of all memp elements in a pool.
|
||||
*/
|
||||
static void
|
||||
memp_overflow_init(const struct memp_desc *desc)
|
||||
{
|
||||
u16_t i;
|
||||
struct memp *p;
|
||||
|
||||
p = (struct memp *)(size_t)(desc->base);
|
||||
for (i = 0; i < desc->num; ++i) {
|
||||
memp_overflow_init_element(p, desc);
|
||||
p = (struct memp*)(size_t)((u8_t*)p + MEMP_SIZE + desc->size + MEMP_SANITY_REGION_AFTER_ALIGNED);
|
||||
}
|
||||
}
|
||||
#endif /* !MEMP_MEM_MALLOC */
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
|
||||
/**
|
||||
* Initialize custom memory pool.
|
||||
* Related functions: memp_malloc_pool, memp_free_pool
|
||||
*
|
||||
* @param desc pool to initialize
|
||||
*/
|
||||
void
|
||||
memp_init_pool(const struct memp_desc *desc)
|
||||
{
|
||||
#if MEMP_MEM_MALLOC
|
||||
LWIP_UNUSED_ARG(desc);
|
||||
#else
|
||||
int i;
|
||||
struct memp *memp;
|
||||
|
||||
*desc->tab = NULL;
|
||||
memp = (struct memp*)LWIP_MEM_ALIGN(desc->base);
|
||||
/* create a linked list of memp elements */
|
||||
for (i = 0; i < desc->num; ++i) {
|
||||
memp->next = *desc->tab;
|
||||
*desc->tab = memp;
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
+ MEMP_SANITY_REGION_AFTER_ALIGNED
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
memp_overflow_init(desc);
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
#endif /* !MEMP_MEM_MALLOC */
|
||||
|
||||
#if MEMP_STATS
|
||||
#if !MEMP_MEM_MALLOC
|
||||
desc->stats->avail = desc->num;
|
||||
#endif /* !MEMP_MEM_MALLOC */
|
||||
|
||||
#if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY
|
||||
desc->stats->name = desc->desc;
|
||||
#endif /* defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY */
|
||||
#endif /* MEMP_STATS */
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes lwIP built-in pools.
|
||||
* Related functions: memp_malloc, memp_free
|
||||
*
|
||||
* Carves out memp_memory into linked lists for each pool-type.
|
||||
*/
|
||||
void
|
||||
memp_init(void)
|
||||
{
|
||||
u16_t i;
|
||||
|
||||
/* for every pool: */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
|
||||
memp_init_pool(memp_pools[i]);
|
||||
|
||||
#if LWIP_STATS && MEMP_STATS
|
||||
lwip_stats.memp[i] = memp_pools[i]->stats;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK >= 2
|
||||
/* check everything a first time to see if it worked */
|
||||
memp_overflow_check_all();
|
||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
||||
}
|
||||
|
||||
static void*
|
||||
#if !MEMP_OVERFLOW_CHECK
|
||||
do_memp_malloc_pool(const struct memp_desc *desc)
|
||||
#else
|
||||
do_memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line)
|
||||
#endif
|
||||
{
|
||||
struct memp *memp;
|
||||
SYS_ARCH_DECL_PROTECT(old_level);
|
||||
|
||||
#if MEMP_MEM_MALLOC
|
||||
memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size));
|
||||
SYS_ARCH_PROTECT(old_level);
|
||||
#else /* MEMP_MEM_MALLOC */
|
||||
SYS_ARCH_PROTECT(old_level);
|
||||
|
||||
memp = *desc->tab;
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK == 1
|
||||
memp_overflow_check_element_overflow(memp, desc);
|
||||
memp_overflow_check_element_underflow(memp, desc);
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
#endif /* MEMP_MEM_MALLOC */
|
||||
|
||||
if (memp != NULL) {
|
||||
#if !MEMP_MEM_MALLOC
|
||||
*desc->tab = memp->next;
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
memp->next = NULL;
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
#endif /* !MEMP_MEM_MALLOC */
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
memp->file = file;
|
||||
memp->line = line;
|
||||
#if MEMP_MEM_MALLOC
|
||||
memp_overflow_init_element(memp, desc);
|
||||
#endif /* MEMP_MEM_MALLOC */
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
LWIP_ASSERT("memp_malloc: memp properly aligned",
|
||||
((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
|
||||
#if MEMP_STATS
|
||||
desc->stats->used++;
|
||||
if (desc->stats->used > desc->stats->max) {
|
||||
desc->stats->max = desc->stats->used;
|
||||
}
|
||||
#endif
|
||||
SYS_ARCH_UNPROTECT(old_level);
|
||||
/* cast through u8_t* to get rid of alignment warnings */
|
||||
return ((u8_t*)memp + MEMP_SIZE);
|
||||
} else {
|
||||
LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
|
||||
#if MEMP_STATS
|
||||
desc->stats->err++;
|
||||
#endif
|
||||
}
|
||||
|
||||
SYS_ARCH_UNPROTECT(old_level);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element from a custom pool.
|
||||
*
|
||||
* @param desc the pool to get an element from
|
||||
*
|
||||
* @return a pointer to the allocated memory or a NULL pointer on error
|
||||
*/
|
||||
void *
|
||||
#if !MEMP_OVERFLOW_CHECK
|
||||
memp_malloc_pool(const struct memp_desc *desc)
|
||||
#else
|
||||
memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line)
|
||||
#endif
|
||||
{
|
||||
LWIP_ASSERT("invalid pool desc", desc != NULL);
|
||||
if (desc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if !MEMP_OVERFLOW_CHECK
|
||||
return do_memp_malloc_pool(desc);
|
||||
#else
|
||||
return do_memp_malloc_pool_fn(desc, file, line);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element from a specific pool.
|
||||
*
|
||||
* @param type the pool to get an element from
|
||||
*
|
||||
* @return a pointer to the allocated memory or a NULL pointer on error
|
||||
*/
|
||||
void *
|
||||
#if !MEMP_OVERFLOW_CHECK
|
||||
memp_malloc(memp_t type)
|
||||
#else
|
||||
memp_malloc_fn(memp_t type, const char* file, const int line)
|
||||
#endif
|
||||
{
|
||||
void *memp;
|
||||
LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK >= 2
|
||||
memp_overflow_check_all();
|
||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
||||
|
||||
#if !MEMP_OVERFLOW_CHECK
|
||||
memp = do_memp_malloc_pool(memp_pools[type]);
|
||||
#else
|
||||
memp = do_memp_malloc_pool_fn(memp_pools[type], file, line);
|
||||
#endif
|
||||
|
||||
return memp;
|
||||
}
|
||||
|
||||
static void
|
||||
do_memp_free_pool(const struct memp_desc* desc, void *mem)
|
||||
{
|
||||
struct memp *memp;
|
||||
SYS_ARCH_DECL_PROTECT(old_level);
|
||||
|
||||
LWIP_ASSERT("memp_free: mem properly aligned",
|
||||
((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
|
||||
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
|
||||
|
||||
SYS_ARCH_PROTECT(old_level);
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK == 1
|
||||
memp_overflow_check_element_overflow(memp, desc);
|
||||
memp_overflow_check_element_underflow(memp, desc);
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
|
||||
#if MEMP_STATS
|
||||
desc->stats->used--;
|
||||
#endif
|
||||
|
||||
#if MEMP_MEM_MALLOC
|
||||
LWIP_UNUSED_ARG(desc);
|
||||
SYS_ARCH_UNPROTECT(old_level);
|
||||
mem_free(memp);
|
||||
#else /* MEMP_MEM_MALLOC */
|
||||
memp->next = *desc->tab;
|
||||
*desc->tab = memp;
|
||||
|
||||
#if MEMP_SANITY_CHECK
|
||||
LWIP_ASSERT("memp sanity", memp_sanity(desc));
|
||||
#endif /* MEMP_SANITY_CHECK */
|
||||
|
||||
SYS_ARCH_UNPROTECT(old_level);
|
||||
#endif /* !MEMP_MEM_MALLOC */
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a custom pool element back into its pool.
|
||||
*
|
||||
* @param desc the pool where to put mem
|
||||
* @param mem the memp element to free
|
||||
*/
|
||||
void
|
||||
memp_free_pool(const struct memp_desc* desc, void *mem)
|
||||
{
|
||||
LWIP_ASSERT("invalid pool desc", desc != NULL);
|
||||
if ((desc == NULL) || (mem == NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
do_memp_free_pool(desc, mem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an element back into its pool.
|
||||
*
|
||||
* @param type the pool where to put mem
|
||||
* @param mem the memp element to free
|
||||
*/
|
||||
void
|
||||
memp_free(memp_t type, void *mem)
|
||||
{
|
||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
||||
struct memp *old_first;
|
||||
#endif
|
||||
|
||||
LWIP_ERROR("memp_free: type < MEMP_MAX", (type < MEMP_MAX), return;);
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK >= 2
|
||||
memp_overflow_check_all();
|
||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
||||
|
||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
||||
old_first = memp_pools[type].tab;
|
||||
#endif
|
||||
|
||||
do_memp_free_pool(memp_pools[type], mem);
|
||||
|
||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
||||
if (old_first == NULL) {
|
||||
LWIP_HOOK_MEMP_AVAILABLE(type);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,11 @@
|
||||
/**
|
||||
* @file
|
||||
* Packet buffer management
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup pbuf Packet buffers (PBUF)
|
||||
* @ingroup infrastructure
|
||||
*
|
||||
* Packets are built from the pbuf data structure. It supports dynamic
|
||||
* memory allocation for packet contents or can reference externally
|
||||
@@ -69,9 +74,8 @@
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "arch/perf.h"
|
||||
#if LWIP_TCP && TCP_QUEUE_OOSEQ
|
||||
#include "lwip/tcp_impl.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#endif
|
||||
#if LWIP_CHECKSUM_ON_COPY
|
||||
#include "lwip/inet_chksum.h"
|
||||
@@ -118,11 +122,7 @@ void
|
||||
pbuf_free_ooseq(void)
|
||||
{
|
||||
struct tcp_pcb* pcb;
|
||||
SYS_ARCH_DECL_PROTECT(old_level);
|
||||
|
||||
SYS_ARCH_PROTECT(old_level);
|
||||
pbuf_free_ooseq_pending = 0;
|
||||
SYS_ARCH_UNPROTECT(old_level);
|
||||
SYS_ARCH_SET(pbuf_free_ooseq_pending, 0);
|
||||
|
||||
for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
|
||||
if (NULL != pcb->ooseq) {
|
||||
@@ -137,7 +137,7 @@ pbuf_free_ooseq(void)
|
||||
|
||||
#if !NO_SYS
|
||||
/**
|
||||
* Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq().
|
||||
* Just a callback function for tcpip_callback() that calls pbuf_free_ooseq().
|
||||
*/
|
||||
static void
|
||||
pbuf_free_ooseq_callback(void *arg)
|
||||
@@ -152,10 +152,7 @@ static void
|
||||
pbuf_pool_is_empty(void)
|
||||
{
|
||||
#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
|
||||
SYS_ARCH_DECL_PROTECT(old_level);
|
||||
SYS_ARCH_PROTECT(old_level);
|
||||
pbuf_free_ooseq_pending = 1;
|
||||
SYS_ARCH_UNPROTECT(old_level);
|
||||
SYS_ARCH_SET(pbuf_free_ooseq_pending, 1);
|
||||
#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
|
||||
u8_t queued;
|
||||
SYS_ARCH_DECL_PROTECT(old_level);
|
||||
@@ -166,13 +163,14 @@ pbuf_pool_is_empty(void)
|
||||
|
||||
if (!queued) {
|
||||
/* queue a call to pbuf_free_ooseq if not already queued */
|
||||
//PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
|
||||
PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
|
||||
}
|
||||
#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
|
||||
}
|
||||
#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
|
||||
*
|
||||
* The actual memory allocated for the pbuf is determined by the
|
||||
@@ -215,17 +213,22 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
|
||||
switch (layer) {
|
||||
case PBUF_TRANSPORT:
|
||||
/* add room for transport (often TCP) layer header */
|
||||
offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
|
||||
break;
|
||||
case PBUF_IP:
|
||||
/* add room for IP layer header */
|
||||
offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN;
|
||||
break;
|
||||
case PBUF_LINK:
|
||||
/* add room for link layer header */
|
||||
offset = PBUF_LINK_HLEN;
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
|
||||
break;
|
||||
case PBUF_RAW_TX:
|
||||
/* add room for encapsulating link layer headers (e.g. 802.11) */
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN;
|
||||
break;
|
||||
case PBUF_RAW:
|
||||
/* no offset (e.g. RX buffers or chain successors) */
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
@@ -274,7 +277,7 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
|
||||
PBUF_POOL_IS_EMPTY();
|
||||
/* free chain so far allocated */
|
||||
pbuf_free(p);
|
||||
/* bail out unsuccesfully */
|
||||
/* bail out unsuccessfully */
|
||||
return NULL;
|
||||
}
|
||||
q->type = type;
|
||||
@@ -349,9 +352,11 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
|
||||
}
|
||||
|
||||
#if LWIP_SUPPORT_CUSTOM_PBUF
|
||||
/** Initialize a custom pbuf (already allocated).
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Initialize a custom pbuf (already allocated).
|
||||
*
|
||||
* @param layer flag to define header size
|
||||
* @param l flag to define header size
|
||||
* @param length size of the pbuf's payload
|
||||
* @param type type of the pbuf (only used to treat the pbuf accordingly, as
|
||||
* this function allocates no memory)
|
||||
@@ -374,15 +379,19 @@ pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_cust
|
||||
switch (l) {
|
||||
case PBUF_TRANSPORT:
|
||||
/* add room for transport (often TCP) layer header */
|
||||
offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
|
||||
break;
|
||||
case PBUF_IP:
|
||||
/* add room for IP layer header */
|
||||
offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN;
|
||||
break;
|
||||
case PBUF_LINK:
|
||||
/* add room for link layer header */
|
||||
offset = PBUF_LINK_HLEN;
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
|
||||
break;
|
||||
case PBUF_RAW_TX:
|
||||
/* add room for encapsulating link layer headers (e.g. 802.11) */
|
||||
offset = PBUF_LINK_ENCAPSULATION_HLEN;
|
||||
break;
|
||||
case PBUF_RAW:
|
||||
offset = 0;
|
||||
@@ -412,6 +421,7 @@ pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_cust
|
||||
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Shrink a pbuf chain to a desired length.
|
||||
*
|
||||
* @param p pbuf to shrink.
|
||||
@@ -468,7 +478,11 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
|
||||
|
||||
/* shrink allocated memory for PBUF_RAM */
|
||||
/* (other types merely adjust their length fields */
|
||||
if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
|
||||
if ((q->type == PBUF_RAM) && (rem_len != q->len)
|
||||
#if LWIP_SUPPORT_CUSTOM_PBUF
|
||||
&& ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0)
|
||||
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
|
||||
) {
|
||||
/* reallocate and adjust the length of the pbuf that will be split */
|
||||
q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
|
||||
LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
|
||||
@@ -489,26 +503,17 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
|
||||
|
||||
/**
|
||||
* Adjusts the payload pointer to hide or reveal headers in the payload.
|
||||
*
|
||||
* Adjusts the ->payload pointer so that space for a header
|
||||
* (dis)appears in the pbuf payload.
|
||||
*
|
||||
* The ->payload, ->tot_len and ->len fields are adjusted.
|
||||
* @see pbuf_header.
|
||||
*
|
||||
* @param p pbuf to change the header size.
|
||||
* @param header_size_increment Number of bytes to increment header size which
|
||||
* increases the size of the pbuf. New space is on the front.
|
||||
* (Using a negative value decreases the header size.)
|
||||
* If hdr_size_inc is 0, this function does nothing and returns succesful.
|
||||
* @param header_size_increment Number of bytes to increment header size.
|
||||
* @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types
|
||||
*
|
||||
* PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
|
||||
* the call will fail. A check is made that the increase in header size does
|
||||
* not move the payload pointer in front of the start of the buffer.
|
||||
* @return non-zero on failure, zero on success.
|
||||
*
|
||||
*/
|
||||
u8_t
|
||||
pbuf_header(struct pbuf *p, s16_t header_size_increment)
|
||||
static u8_t
|
||||
pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
|
||||
{
|
||||
u16_t type;
|
||||
void *payload;
|
||||
@@ -547,23 +552,25 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
|
||||
p->payload = (u8_t *)p->payload - header_size_increment;
|
||||
/* boundary check fails? */
|
||||
if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
|
||||
LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
|
||||
LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE,
|
||||
("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
|
||||
(void *)p->payload, (void *)(p + 1)));
|
||||
(void *)p->payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF)));
|
||||
/* restore old payload pointer */
|
||||
p->payload = payload;
|
||||
/* bail out unsuccesfully */
|
||||
/* bail out unsuccessfully */
|
||||
return 1;
|
||||
}
|
||||
/* pbuf types refering to external payloads? */
|
||||
/* pbuf types referring to external payloads? */
|
||||
} else if (type == PBUF_REF || type == PBUF_ROM) {
|
||||
/* hide a header in the payload? */
|
||||
if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
|
||||
/* increase payload pointer */
|
||||
p->payload = (u8_t *)p->payload - header_size_increment;
|
||||
} else if ((header_size_increment > 0) && force) {
|
||||
p->payload = (u8_t *)p->payload - header_size_increment;
|
||||
} else {
|
||||
/* cannot expand payload to front (yet!)
|
||||
* bail out unsuccesfully */
|
||||
* bail out unsuccessfully */
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
@@ -582,6 +589,43 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the payload pointer to hide or reveal headers in the payload.
|
||||
*
|
||||
* Adjusts the ->payload pointer so that space for a header
|
||||
* (dis)appears in the pbuf payload.
|
||||
*
|
||||
* The ->payload, ->tot_len and ->len fields are adjusted.
|
||||
*
|
||||
* @param p pbuf to change the header size.
|
||||
* @param header_size_increment Number of bytes to increment header size which
|
||||
* increases the size of the pbuf. New space is on the front.
|
||||
* (Using a negative value decreases the header size.)
|
||||
* If hdr_size_inc is 0, this function does nothing and returns successful.
|
||||
*
|
||||
* PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
|
||||
* the call will fail. A check is made that the increase in header size does
|
||||
* not move the payload pointer in front of the start of the buffer.
|
||||
* @return non-zero on failure, zero on success.
|
||||
*
|
||||
*/
|
||||
u8_t
|
||||
pbuf_header(struct pbuf *p, s16_t header_size_increment)
|
||||
{
|
||||
return pbuf_header_impl(p, header_size_increment, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as pbuf_header but does not check if 'header_size > 0' is allowed.
|
||||
* This is used internally only, to allow PBUF_REF for RX.
|
||||
*/
|
||||
u8_t
|
||||
pbuf_header_force(struct pbuf *p, s16_t header_size_increment)
|
||||
{
|
||||
return pbuf_header_impl(p, header_size_increment, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Dereference a pbuf chain or queue and deallocate any no-longer-used
|
||||
* pbufs at the head of this chain or queue.
|
||||
*
|
||||
@@ -699,7 +743,6 @@ pbuf_free(struct pbuf *p)
|
||||
* @param p first pbuf of chain
|
||||
* @return the number of pbufs in a chain
|
||||
*/
|
||||
|
||||
u8_t
|
||||
pbuf_clen(struct pbuf *p)
|
||||
{
|
||||
@@ -714,6 +757,7 @@ pbuf_clen(struct pbuf *p)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Increment the reference count of the pbuf.
|
||||
*
|
||||
* @param p pbuf to increase reference counter of
|
||||
@@ -732,6 +776,7 @@ pbuf_ref(struct pbuf *p)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Concatenate two pbufs (each may be a pbuf chain) and take over
|
||||
* the caller's reference of the tail pbuf.
|
||||
*
|
||||
@@ -740,7 +785,6 @@ pbuf_ref(struct pbuf *p)
|
||||
*
|
||||
* @see pbuf_chain()
|
||||
*/
|
||||
|
||||
void
|
||||
pbuf_cat(struct pbuf *h, struct pbuf *t)
|
||||
{
|
||||
@@ -767,6 +811,7 @@ pbuf_cat(struct pbuf *h, struct pbuf *t)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Chain two pbufs (or pbuf chains) together.
|
||||
*
|
||||
* The caller MUST call pbuf_free(t) once it has stopped
|
||||
@@ -831,7 +876,7 @@ pbuf_dechain(struct pbuf *p)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @ingroup pbuf
|
||||
* Create PBUF_RAM copies of pbufs.
|
||||
*
|
||||
* Used to queue packets on behalf of the lwIP stack, such as
|
||||
@@ -890,12 +935,12 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
|
||||
|
||||
if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
|
||||
/* don't copy more than one packet! */
|
||||
LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
|
||||
LWIP_ERROR("pbuf_copy() does not allow packet queues!",
|
||||
(p_from->next == NULL), return ERR_VAL;);
|
||||
}
|
||||
if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
|
||||
/* don't copy more than one packet! */
|
||||
LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
|
||||
LWIP_ERROR("pbuf_copy() does not allow packet queues!",
|
||||
(p_to->next == NULL), return ERR_VAL;);
|
||||
}
|
||||
} while (p_from);
|
||||
@@ -904,6 +949,7 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Copy (part of) the contents of a packet buffer
|
||||
* to an application supplied buffer.
|
||||
*
|
||||
@@ -952,7 +998,84 @@ pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
|
||||
return copied_total;
|
||||
}
|
||||
|
||||
#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
|
||||
/**
|
||||
* This method modifies a 'pbuf chain', so that its total length is
|
||||
* smaller than 64K. The remainder of the original pbuf chain is stored
|
||||
* in *rest.
|
||||
* This function never creates new pbufs, but splits an existing chain
|
||||
* in two parts. The tot_len of the modified packet queue will likely be
|
||||
* smaller than 64K.
|
||||
* 'packet queues' are not supported by this function.
|
||||
*
|
||||
* @param p the pbuf queue to be split
|
||||
* @param rest pointer to store the remainder (after the first 64K)
|
||||
*/
|
||||
void pbuf_split_64k(struct pbuf *p, struct pbuf **rest)
|
||||
{
|
||||
*rest = NULL;
|
||||
if ((p != NULL) && (p->next != NULL)) {
|
||||
u16_t tot_len_front = p->len;
|
||||
struct pbuf *i = p;
|
||||
struct pbuf *r = p->next;
|
||||
|
||||
/* continue until the total length (summed up as u16_t) overflows */
|
||||
while ((r != NULL) && ((u16_t)(tot_len_front + r->len) > tot_len_front)) {
|
||||
tot_len_front += r->len;
|
||||
i = r;
|
||||
r = r->next;
|
||||
}
|
||||
/* i now points to last packet of the first segment. Set next
|
||||
pointer to NULL */
|
||||
i->next = NULL;
|
||||
|
||||
if (r != NULL) {
|
||||
/* Update the tot_len field in the first part */
|
||||
for (i = p; i != NULL; i = i->next) {
|
||||
i->tot_len -= r->tot_len;
|
||||
LWIP_ASSERT("tot_len/len mismatch in last pbuf",
|
||||
(i->next != NULL) || (i->tot_len == i->len));
|
||||
}
|
||||
if (p->flags & PBUF_FLAG_TCP_FIN) {
|
||||
r->flags |= PBUF_FLAG_TCP_FIN;
|
||||
}
|
||||
|
||||
/* tot_len field in rest does not need modifications */
|
||||
/* reference counters do not need modifications */
|
||||
*rest = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Skip a number of bytes at the start of a pbuf
|
||||
*
|
||||
* @param in input pbuf
|
||||
* @param in_offset offset to skip
|
||||
* @param out_offset resulting offset in the returned pbuf
|
||||
* @return the pbuf in the queue where the offset is
|
||||
*/
|
||||
struct pbuf*
|
||||
pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset)
|
||||
{
|
||||
u16_t offset_left = in_offset;
|
||||
struct pbuf* q = in;
|
||||
|
||||
/* get the correct pbuf */
|
||||
while ((q != NULL) && (q->len <= offset_left)) {
|
||||
offset_left -= q->len;
|
||||
q = q->next;
|
||||
}
|
||||
if (out_offset != NULL) {
|
||||
*out_offset = offset_left;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Copy application supplied data into a pbuf.
|
||||
* This function can only be used to copy the equivalent of buf->tot_len data.
|
||||
*
|
||||
@@ -970,8 +1093,9 @@ pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
|
||||
u16_t total_copy_len = len;
|
||||
u16_t copied_total = 0;
|
||||
|
||||
LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
|
||||
LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
|
||||
LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;);
|
||||
|
||||
if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
|
||||
return ERR_ARG;
|
||||
@@ -986,7 +1110,7 @@ pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
|
||||
buf_copy_len = p->len;
|
||||
}
|
||||
/* copy the necessary parts of the buffer */
|
||||
MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
|
||||
MEMCPY(p->payload, &((const char*)dataptr)[copied_total], buf_copy_len);
|
||||
total_copy_len -= buf_copy_len;
|
||||
copied_total += buf_copy_len;
|
||||
}
|
||||
@@ -995,6 +1119,41 @@ pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Same as pbuf_take() but puts data at an offset
|
||||
*
|
||||
* @param buf pbuf to fill with data
|
||||
* @param dataptr application supplied data buffer
|
||||
* @param len length of the application supplied data buffer
|
||||
* @param offset offset in pbuf where to copy dataptr to
|
||||
*
|
||||
* @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
|
||||
*/
|
||||
err_t
|
||||
pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset)
|
||||
{
|
||||
u16_t target_offset;
|
||||
struct pbuf* q = pbuf_skip(buf, offset, &target_offset);
|
||||
|
||||
/* return requested data if pbuf is OK */
|
||||
if ((q != NULL) && (q->tot_len >= target_offset + len)) {
|
||||
u16_t remaining_len = len;
|
||||
const u8_t* src_ptr = (const u8_t*)dataptr;
|
||||
/* copy the part that goes into the first pbuf */
|
||||
u16_t first_copy_len = LWIP_MIN(q->len - target_offset, len);
|
||||
MEMCPY(((u8_t*)q->payload) + target_offset, dataptr, first_copy_len);
|
||||
remaining_len -= first_copy_len;
|
||||
src_ptr += first_copy_len;
|
||||
if (remaining_len > 0) {
|
||||
return pbuf_take(q->next, src_ptr, remaining_len);
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Creates a single pbuf out of a queue of pbufs.
|
||||
*
|
||||
* @remark: Either the source pbuf 'p' is freed by this function or the original
|
||||
@@ -1066,7 +1225,9 @@ pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
|
||||
}
|
||||
#endif /* LWIP_CHECKSUM_ON_COPY */
|
||||
|
||||
/** Get one byte from the specified position in a pbuf
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Get one byte from the specified position in a pbuf
|
||||
* WARNING: returns zero for offset >= p->tot_len
|
||||
*
|
||||
* @param p pbuf to parse
|
||||
@@ -1076,25 +1237,43 @@ pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
|
||||
u8_t
|
||||
pbuf_get_at(struct pbuf* p, u16_t offset)
|
||||
{
|
||||
u16_t copy_from = offset;
|
||||
struct pbuf* q = p;
|
||||
u16_t q_idx;
|
||||
struct pbuf* q = pbuf_skip(p, offset, &q_idx);
|
||||
|
||||
/* get the correct pbuf */
|
||||
while ((q != NULL) && (q->len <= copy_from)) {
|
||||
copy_from -= q->len;
|
||||
q = q->next;
|
||||
}
|
||||
/* return requested data if pbuf is OK */
|
||||
if ((q != NULL) && (q->len > copy_from)) {
|
||||
return ((u8_t*)q->payload)[copy_from];
|
||||
if ((q != NULL) && (q->len > q_idx)) {
|
||||
return ((u8_t*)q->payload)[q_idx];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Compare pbuf contents at specified offset with memory s2, both of length n
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Put one byte to the specified position in a pbuf
|
||||
* WARNING: silently ignores offset >= p->tot_len
|
||||
*
|
||||
* @param p pbuf to fill
|
||||
* @param offset offset into p of the byte to write
|
||||
* @param data byte to write at an offset into p
|
||||
*/
|
||||
void
|
||||
pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data)
|
||||
{
|
||||
u16_t q_idx;
|
||||
struct pbuf* q = pbuf_skip(p, offset, &q_idx);
|
||||
|
||||
/* write requested data if pbuf is OK */
|
||||
if ((q != NULL) && (q->len > q_idx)) {
|
||||
((u8_t*)q->payload)[q_idx] = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Compare pbuf contents at specified offset with memory s2, both of length n
|
||||
*
|
||||
* @param p pbuf to compare
|
||||
* @param offset offset into p at wich to start comparing
|
||||
* @param offset offset into p at which to start comparing
|
||||
* @param s2 buffer to compare
|
||||
* @param n length of buffer to compare
|
||||
* @return zero if equal, nonzero otherwise
|
||||
@@ -1116,7 +1295,7 @@ pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
|
||||
u16_t i;
|
||||
for (i = 0; i < n; i++) {
|
||||
u8_t a = pbuf_get_at(q, start + i);
|
||||
u8_t b = ((u8_t*)s2)[i];
|
||||
u8_t b = ((const u8_t*)s2)[i];
|
||||
if (a != b) {
|
||||
return i+1;
|
||||
}
|
||||
@@ -1126,7 +1305,9 @@ pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
|
||||
/**
|
||||
* @ingroup pbuf
|
||||
* Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
|
||||
* start_offset.
|
||||
*
|
||||
* @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
|
||||
@@ -1142,19 +1323,18 @@ pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
|
||||
u16_t i;
|
||||
u16_t max = p->tot_len - mem_len;
|
||||
if (p->tot_len >= mem_len + start_offset) {
|
||||
for(i = start_offset; i <= max; ) {
|
||||
for (i = start_offset; i <= max; i++) {
|
||||
u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
|
||||
if (plus == 0) {
|
||||
return i;
|
||||
} else {
|
||||
i += plus;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
/** Find occurrence of substr with length substr_len in pbuf p, start at offset
|
||||
/**
|
||||
* Find occurrence of substr with length substr_len in pbuf p, start at offset
|
||||
* start_offset
|
||||
* WARNING: in contrast to strstr(), this one does not stop at the first \0 in
|
||||
* the pbuf/source string!
|
||||
@@ -2,8 +2,8 @@
|
||||
* @file
|
||||
* Implementation of raw protocol PCBs for low-level handling of
|
||||
* different types of protocols besides (or overriding) those
|
||||
* already available in lwIP.
|
||||
*
|
||||
* already available in lwIP.\n
|
||||
* See also @ref raw_raw
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -38,6 +38,21 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup raw_api RAW API
|
||||
* @ingroup callbackstyle_api
|
||||
* @verbinclude "rawapi.txt"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup raw_raw RAW
|
||||
* @ingroup raw_api
|
||||
* Implementation of raw protocol PCBs for low-level handling of
|
||||
* different types of protocols besides (or overriding) those
|
||||
* already available in lwIP.\n
|
||||
* @see @ref raw_api
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
|
||||
@@ -48,13 +63,58 @@
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "arch/perf.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/** The list of RAW PCBs */
|
||||
static struct raw_pcb *raw_pcbs;
|
||||
|
||||
static u8_t
|
||||
raw_input_match(struct raw_pcb *pcb, u8_t broadcast)
|
||||
{
|
||||
LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
/* Dual-stack: PCBs listening to any IP type also listen to any IP address */
|
||||
if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
|
||||
#if IP_SOF_BROADCAST_RECV
|
||||
if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
|
||||
return 0;
|
||||
}
|
||||
#endif /* IP_SOF_BROADCAST_RECV */
|
||||
return 1;
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
|
||||
/* Only need to check PCB if incoming IP version matches PCB IP version */
|
||||
if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
|
||||
#if LWIP_IPV4
|
||||
/* Special case: IPv4 broadcast: receive all broadcasts
|
||||
* Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
|
||||
if (broadcast != 0) {
|
||||
#if IP_SOF_BROADCAST_RECV
|
||||
if (ip_get_option(pcb, SOF_BROADCAST))
|
||||
#endif /* IP_SOF_BROADCAST_RECV */
|
||||
{
|
||||
if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif /* LWIP_IPV4 */
|
||||
/* Handle IPv4 and IPv6: catch all or exact match */
|
||||
if (ip_addr_isany(&pcb->local_ip) ||
|
||||
ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if in incoming IP packet is covered by a RAW PCB
|
||||
* and if so, pass it to a user-provided receive callback function.
|
||||
@@ -76,32 +136,44 @@ u8_t
|
||||
raw_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
struct raw_pcb *pcb, *prev;
|
||||
struct ip_hdr *iphdr;
|
||||
s16_t proto;
|
||||
u8_t eaten = 0;
|
||||
u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
|
||||
|
||||
LWIP_UNUSED_ARG(inp);
|
||||
|
||||
iphdr = (struct ip_hdr *)p->payload;
|
||||
proto = IPH_PROTO(iphdr);
|
||||
#if LWIP_IPV6
|
||||
#if LWIP_IPV4
|
||||
if (IP_HDR_GET_VERSION(p->payload) == 6)
|
||||
#endif /* LWIP_IPV4 */
|
||||
{
|
||||
struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
|
||||
proto = IP6H_NEXTH(ip6hdr);
|
||||
}
|
||||
#if LWIP_IPV4
|
||||
else
|
||||
#endif /* LWIP_IPV4 */
|
||||
#endif /* LWIP_IPV6 */
|
||||
#if LWIP_IPV4
|
||||
{
|
||||
proto = IPH_PROTO((struct ip_hdr *)p->payload);
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
prev = NULL;
|
||||
pcb = raw_pcbs;
|
||||
/* loop through all raw pcbs until the packet is eaten by one */
|
||||
/* this allows multiple pcbs to match against the packet by design */
|
||||
while ((eaten == 0) && (pcb != NULL)) {
|
||||
if ((pcb->protocol == proto) &&
|
||||
(ip_addr_isany(&pcb->local_ip) ||
|
||||
ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest))) {
|
||||
#if IP_SOF_BROADCAST_RECV
|
||||
/* broadcast filter? */
|
||||
if (ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(¤t_iphdr_dest, inp))
|
||||
#endif /* IP_SOF_BROADCAST_RECV */
|
||||
{
|
||||
if ((pcb->protocol == proto) && raw_input_match(pcb, broadcast)) {
|
||||
/* receive callback function available? */
|
||||
if (pcb->recv != NULL) {
|
||||
#ifndef LWIP_NOASSERT
|
||||
void* old_payload = p->payload;
|
||||
#endif
|
||||
/* the receive callback function did not eat the packet? */
|
||||
if (pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()) != 0) {
|
||||
eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr());
|
||||
if (eaten != 0) {
|
||||
/* receive function ate the packet */
|
||||
p = NULL;
|
||||
eaten = 1;
|
||||
@@ -112,12 +184,15 @@ raw_input(struct pbuf *p, struct netif *inp)
|
||||
pcb->next = raw_pcbs;
|
||||
raw_pcbs = pcb;
|
||||
}
|
||||
} else {
|
||||
/* sanity-check that the receive callback did not alter the pbuf */
|
||||
LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
|
||||
p->payload == old_payload);
|
||||
}
|
||||
}
|
||||
/* no receive callback function was set for this raw PCB */
|
||||
}
|
||||
/* drop the packet */
|
||||
}
|
||||
prev = pcb;
|
||||
pcb = pcb->next;
|
||||
}
|
||||
@@ -125,6 +200,7 @@ raw_input(struct pbuf *p, struct netif *inp)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Bind a RAW PCB.
|
||||
*
|
||||
* @param pcb RAW PCB to be bound with a local address ipaddr.
|
||||
@@ -132,20 +208,24 @@ raw_input(struct pbuf *p, struct netif *inp)
|
||||
* bind to all local interfaces.
|
||||
*
|
||||
* @return lwIP error code.
|
||||
* - ERR_OK. Successful. No error occured.
|
||||
* - ERR_OK. Successful. No error occurred.
|
||||
* - ERR_USE. The specified IP address is already bound to by
|
||||
* another RAW PCB.
|
||||
*
|
||||
* @see raw_disconnect()
|
||||
*/
|
||||
err_t
|
||||
raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr)
|
||||
raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
|
||||
{
|
||||
ip_addr_set(&pcb->local_ip, ipaddr);
|
||||
if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Connect an RAW PCB. This function is required by upper layers
|
||||
* of lwip. Using the raw api you could use raw_sendto() instead
|
||||
*
|
||||
@@ -159,14 +239,17 @@ raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr)
|
||||
* @see raw_disconnect() and raw_sendto()
|
||||
*/
|
||||
err_t
|
||||
raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr)
|
||||
raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
|
||||
{
|
||||
ip_addr_set(&pcb->remote_ip, ipaddr);
|
||||
if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Set the callback function for received packets that match the
|
||||
* raw PCB's protocol and binding.
|
||||
*
|
||||
@@ -188,6 +271,7 @@ raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Send the raw IP packet to the given address. Note that actually you cannot
|
||||
* modify the IP headers (this is inconsistent with the receive callback where
|
||||
* you actually get the IP headers), you can only specify the IP payload here.
|
||||
@@ -200,17 +284,32 @@ raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
|
||||
*
|
||||
*/
|
||||
err_t
|
||||
raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
|
||||
raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
|
||||
{
|
||||
err_t err;
|
||||
struct netif *netif;
|
||||
ip_addr_t *src_ip;
|
||||
const ip_addr_t *src_ip;
|
||||
struct pbuf *q; /* q will be sent down the stack */
|
||||
s16_t header_size;
|
||||
const ip_addr_t *dst_ip = ipaddr;
|
||||
|
||||
if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
|
||||
|
||||
header_size = (
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
IP_IS_V6(ipaddr) ? IP6_HLEN : IP_HLEN);
|
||||
#elif LWIP_IPV4
|
||||
IP_HLEN);
|
||||
#else
|
||||
IP6_HLEN);
|
||||
#endif
|
||||
|
||||
/* not enough space to add an IP header to first pbuf in given p chain? */
|
||||
if (pbuf_header(p, IP_HLEN)) {
|
||||
if (pbuf_header(p, header_size)) {
|
||||
/* allocate header in new pbuf */
|
||||
q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
|
||||
/* new header pbuf could not be allocated? */
|
||||
@@ -227,15 +326,16 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
|
||||
} else {
|
||||
/* first pbuf q equals given pbuf */
|
||||
q = p;
|
||||
if(pbuf_header(q, -IP_HLEN)) {
|
||||
if (pbuf_header(q, -header_size)) {
|
||||
LWIP_ASSERT("Can't restore header we just removed!", 0);
|
||||
return ERR_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
if ((netif = ip_route(ipaddr)) == NULL) {
|
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
|
||||
netif = ip_route(&pcb->local_ip, dst_ip);
|
||||
if (netif == NULL) {
|
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
|
||||
ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip);
|
||||
/* free any temporary header pbuf allocated by pbuf_header() */
|
||||
if (q != p) {
|
||||
pbuf_free(q);
|
||||
@@ -244,6 +344,8 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
|
||||
}
|
||||
|
||||
#if IP_SOF_BROADCAST
|
||||
if (IP_IS_V4(ipaddr))
|
||||
{
|
||||
/* broadcast filter? */
|
||||
if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
|
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
|
||||
@@ -253,18 +355,37 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
}
|
||||
#endif /* IP_SOF_BROADCAST */
|
||||
|
||||
if (ip_addr_isany(&pcb->local_ip)) {
|
||||
/* use outgoing network interface IP address as source address */
|
||||
src_ip = &(netif->ip_addr);
|
||||
src_ip = ip_netif_get_local_ip(netif, dst_ip);
|
||||
#if LWIP_IPV6
|
||||
if (src_ip == NULL) {
|
||||
if (q != p) {
|
||||
pbuf_free(q);
|
||||
}
|
||||
return ERR_RTE;
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
} else {
|
||||
/* use RAW PCB local IP address as source address */
|
||||
src_ip = &(pcb->local_ip);
|
||||
src_ip = &pcb->local_ip;
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
/* If requested, based on the IPV6_CHECKSUM socket option per RFC3542,
|
||||
compute the checksum and update the checksum in the payload. */
|
||||
if (IP_IS_V6(dst_ip) && pcb->chksum_reqd) {
|
||||
u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(dst_ip));
|
||||
LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2));
|
||||
SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
|
||||
err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
|
||||
err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, pcb->protocol, netif);
|
||||
NETIF_SET_HWADDRHINT(netif, NULL);
|
||||
|
||||
/* did we chain a header earlier? */
|
||||
@@ -276,6 +397,7 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Send the raw IP packet to the address given by raw_connect()
|
||||
*
|
||||
* @param pcb the raw pcb which to send
|
||||
@@ -289,6 +411,7 @@ raw_send(struct raw_pcb *pcb, struct pbuf *p)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Remove an RAW PCB.
|
||||
*
|
||||
* @param pcb RAW PCB to be removed. The PCB is removed from the list of
|
||||
@@ -311,6 +434,7 @@ raw_remove(struct raw_pcb *pcb)
|
||||
if (pcb2->next != NULL && pcb2->next == pcb) {
|
||||
/* remove pcb from list */
|
||||
pcb2->next = pcb->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -318,6 +442,7 @@ raw_remove(struct raw_pcb *pcb)
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Create a RAW PCB.
|
||||
*
|
||||
* @return The RAW PCB which was created. NULL if the PCB data structure
|
||||
@@ -347,4 +472,33 @@ raw_new(u8_t proto)
|
||||
return pcb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup raw_raw
|
||||
* Create a RAW PCB for specific IP type.
|
||||
*
|
||||
* @return The RAW PCB which was created. NULL if the PCB data structure
|
||||
* could not be allocated.
|
||||
*
|
||||
* @param type IP address type, see IPADDR_TYPE_XX definitions.
|
||||
* @param proto the protocol number (next header) of the IPv6 packet payload
|
||||
* (e.g. IP6_NEXTH_ICMP6)
|
||||
*
|
||||
* @see raw_remove()
|
||||
*/
|
||||
struct raw_pcb *
|
||||
raw_new_ip_type(u8_t type, u8_t proto)
|
||||
{
|
||||
struct raw_pcb *pcb;
|
||||
pcb = raw_new(proto);
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
if (pcb != NULL) {
|
||||
IP_SET_TYPE_VAL(pcb->local_ip, type);
|
||||
IP_SET_TYPE_VAL(pcb->remote_ip, type);
|
||||
}
|
||||
#else /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
LWIP_UNUSED_ARG(type);
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
return pcb;
|
||||
}
|
||||
|
||||
#endif /* LWIP_RAW */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@
|
||||
|
||||
#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/tcp_impl.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/memp.h"
|
||||
@@ -50,7 +50,9 @@
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#if LWIP_TCP_TIMESTAMPS
|
||||
#include "lwip/sys.h"
|
||||
#endif
|
||||
@@ -76,9 +78,22 @@
|
||||
#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
|
||||
#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0
|
||||
#endif
|
||||
/* Allow to override the failure of sanity check from warning to e.g. hard failure */
|
||||
#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
|
||||
#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL
|
||||
#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(msg) LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, msg)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if TCP_OVERSIZE
|
||||
/** The size of segment pbufs created when TCP_OVERSIZE is enabled */
|
||||
#ifndef TCP_OVERSIZE_CALC_LENGTH
|
||||
#define TCP_OVERSIZE_CALC_LENGTH(length) ((length) + TCP_OVERSIZE)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Forward declarations.*/
|
||||
static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
|
||||
static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif);
|
||||
|
||||
/** Allocate a pbuf and create a tcphdr at p->payload, used for output
|
||||
* functions other than the default tcp_output -> tcp_output_segment
|
||||
@@ -105,7 +120,7 @@ tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
|
||||
tcphdr->seqno = seqno_be;
|
||||
tcphdr->ackno = htonl(pcb->rcv_nxt);
|
||||
TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
|
||||
tcphdr->wnd = htons(pcb->rcv_ann_wnd);
|
||||
tcphdr->wnd = htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
|
||||
tcphdr->chksum = 0;
|
||||
tcphdr->urgp = 0;
|
||||
|
||||
@@ -162,13 +177,14 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno,
|
||||
u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
|
||||
|
||||
if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no memory.\n"));
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
seg->flags = optflags;
|
||||
seg->next = NULL;
|
||||
seg->p = p;
|
||||
LWIP_ASSERT("p->tot_len >= optlen", p->tot_len >= optlen);
|
||||
seg->len = p->tot_len - optlen;
|
||||
#if TCP_OVERSIZE_DBGCHECK
|
||||
seg->oversize_left = 0;
|
||||
@@ -183,7 +199,7 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno,
|
||||
|
||||
/* build TCP header */
|
||||
if (pbuf_header(p, TCP_HLEN)) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
|
||||
TCP_STATS_INC(tcp.err);
|
||||
tcp_seg_free(seg);
|
||||
return NULL;
|
||||
@@ -212,7 +228,6 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno,
|
||||
* @param pcb The TCP connection that willo enqueue the pbuf.
|
||||
* @param apiflags API flags given to tcp_write.
|
||||
* @param first_seg true when this pbuf will be used in the first enqueued segment.
|
||||
* @param
|
||||
*/
|
||||
#if TCP_OVERSIZE
|
||||
static struct pbuf *
|
||||
@@ -248,7 +263,7 @@ tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
|
||||
(!first_seg ||
|
||||
pcb->unsent != NULL ||
|
||||
pcb->unacked != NULL))) {
|
||||
alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
|
||||
alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(TCP_OVERSIZE_CALC_LENGTH(length)));
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
|
||||
@@ -306,20 +321,20 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
|
||||
|
||||
/* fail on too much data */
|
||||
if (len > pcb->snd_buf) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"TCPWNDSIZE_F")\n",
|
||||
len, pcb->snd_buf));
|
||||
pcb->flags |= TF_NAGLEMEMERR;
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
|
||||
|
||||
/* If total number of pbufs on the unsent/unacked queues exceeds the
|
||||
* configured maximum, return an error */
|
||||
/* check for configured max queuelen and possible overflow */
|
||||
if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
|
||||
pcb->snd_queuelen, TCP_SND_QUEUELEN));
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
|
||||
pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
|
||||
TCP_STATS_INC(tcp.memerr);
|
||||
pcb->flags |= TF_NAGLEMEMERR;
|
||||
return ERR_MEM;
|
||||
@@ -347,7 +362,7 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
|
||||
* @param len Data length in bytes
|
||||
* @param apiflags combination of following flags :
|
||||
* - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
|
||||
* - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
|
||||
* - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent,
|
||||
* @return ERR_OK if enqueued, another err_t on error
|
||||
*/
|
||||
err_t
|
||||
@@ -370,10 +385,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
#endif /* TCP_CHECKSUM_ON_COPY */
|
||||
err_t err;
|
||||
/* don't allocate segments bigger than half the maximum window we ever received */
|
||||
u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2);
|
||||
|
||||
// Added to fix bug where mss_local was 0 - Joseph Henry
|
||||
mss_local = mss_local == 0 ? pcb->mss : mss_local;
|
||||
u16_t mss_local = LWIP_MIN(pcb->mss, TCPWND_MIN16(pcb->snd_wnd_max/2));
|
||||
mss_local = mss_local ? mss_local : pcb->mss;
|
||||
|
||||
#if LWIP_NETIF_TX_SINGLE_PBUF
|
||||
/* Always copy to try to create single pbufs for TX */
|
||||
@@ -393,8 +406,12 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
|
||||
#if LWIP_TCP_TIMESTAMPS
|
||||
if ((pcb->flags & TF_TIMESTAMP)) {
|
||||
/* Make sure the timestamp option is only included in data segments if we
|
||||
agreed about it with the remote host. */
|
||||
optflags = TF_SEG_OPTS_TS;
|
||||
optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
|
||||
/* ensure that segments can hold at least one data byte... */
|
||||
mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1);
|
||||
}
|
||||
#endif /* LWIP_TCP_TIMESTAMPS */
|
||||
|
||||
@@ -432,6 +449,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
|
||||
/* Usable space at the end of the last unsent segment */
|
||||
unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
|
||||
LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen);
|
||||
space = mss_local - (last_unsent->len + unsent_optlen);
|
||||
|
||||
/*
|
||||
@@ -451,7 +469,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
if (oversize > 0) {
|
||||
LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
|
||||
seg = last_unsent;
|
||||
oversize_used = oversize < len ? oversize : len;
|
||||
oversize_used = LWIP_MIN(space, LWIP_MIN(oversize, len));
|
||||
pos += oversize_used;
|
||||
oversize -= oversize_used;
|
||||
space -= oversize_used;
|
||||
@@ -477,7 +495,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
if (apiflags & TCP_WRITE_FLAG_COPY) {
|
||||
/* Data is copied */
|
||||
if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
|
||||
("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
|
||||
seglen));
|
||||
goto memerr;
|
||||
@@ -485,25 +503,25 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
#if TCP_OVERSIZE_DBGCHECK
|
||||
last_unsent->oversize_left += oversize;
|
||||
#endif /* TCP_OVERSIZE_DBGCHECK */
|
||||
TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
|
||||
TCP_DATA_COPY2(concat_p->payload, (const u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
|
||||
#if TCP_CHECKSUM_ON_COPY
|
||||
concat_chksummed += seglen;
|
||||
#endif /* TCP_CHECKSUM_ON_COPY */
|
||||
} else {
|
||||
/* Data is not copied */
|
||||
if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
|
||||
("tcp_write: could not allocate memory for zero-copy pbuf\n"));
|
||||
goto memerr;
|
||||
}
|
||||
#if TCP_CHECKSUM_ON_COPY
|
||||
/* calculate the checksum of nocopy-data */
|
||||
tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen,
|
||||
tcp_seg_add_chksum(~inet_chksum((const u8_t*)arg + pos, seglen), seglen,
|
||||
&concat_chksum, &concat_chksum_swapped);
|
||||
concat_chksummed += seglen;
|
||||
#endif /* TCP_CHECKSUM_ON_COPY */
|
||||
/* reference the non-volatile payload data */
|
||||
concat_p->payload = (u8_t*)arg + pos;
|
||||
((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos;
|
||||
}
|
||||
|
||||
pos += seglen;
|
||||
@@ -536,12 +554,12 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
/* If copy is set, memory should be allocated and data copied
|
||||
* into pbuf */
|
||||
if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
|
||||
goto memerr;
|
||||
}
|
||||
LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
|
||||
(p->len >= seglen));
|
||||
TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
|
||||
TCP_DATA_COPY2((char *)p->payload + optlen, (const u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
|
||||
} else {
|
||||
/* Copy is not set: First allocate a pbuf for holding the data.
|
||||
* Since the referenced data is available at least until it is
|
||||
@@ -553,22 +571,26 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
LWIP_ASSERT("oversize == 0", oversize == 0);
|
||||
#endif /* TCP_OVERSIZE */
|
||||
if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
|
||||
goto memerr;
|
||||
}
|
||||
#if TCP_CHECKSUM_ON_COPY
|
||||
/* calculate the checksum of nocopy-data */
|
||||
chksum = ~inet_chksum((u8_t*)arg + pos, seglen);
|
||||
chksum = ~inet_chksum((const u8_t*)arg + pos, seglen);
|
||||
if (seglen & 1) {
|
||||
chksum_swapped = 1;
|
||||
chksum = SWAP_BYTES_IN_WORD(chksum);
|
||||
}
|
||||
#endif /* TCP_CHECKSUM_ON_COPY */
|
||||
/* reference the non-volatile payload data */
|
||||
p2->payload = (u8_t*)arg + pos;
|
||||
((struct pbuf_rom*)p2)->payload = (const u8_t*)arg + pos;
|
||||
|
||||
/* Second, allocate a pbuf for the headers. */
|
||||
if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
|
||||
/* If allocation fails, we have to deallocate the data pbuf as
|
||||
* well. */
|
||||
pbuf_free(p2);
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for header pbuf\n"));
|
||||
goto memerr;
|
||||
}
|
||||
/* Concatenate the headers and data pbufs together. */
|
||||
@@ -581,7 +603,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
* length of the queue exceeds the configured maximum or
|
||||
* overflows. */
|
||||
if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n",
|
||||
queuelen, (int)TCP_SND_QUEUELEN));
|
||||
pbuf_free(p);
|
||||
goto memerr;
|
||||
}
|
||||
@@ -656,6 +679,10 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
||||
last_unsent->len += concat_p->tot_len;
|
||||
#if TCP_CHECKSUM_ON_COPY
|
||||
if (concat_chksummed) {
|
||||
/*if concat checksumm swapped - swap it back */
|
||||
if (concat_chksum_swapped) {
|
||||
concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
|
||||
}
|
||||
tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
|
||||
&last_unsent->chksum_swapped);
|
||||
last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
|
||||
@@ -718,8 +745,6 @@ memerr:
|
||||
*
|
||||
* @param pcb Protocol control block for the TCP connection.
|
||||
* @param flags TCP header flags to set in the outgoing segment.
|
||||
* @param optdata pointer to TCP options, or NULL.
|
||||
* @param optlen length of TCP options in bytes.
|
||||
*/
|
||||
err_t
|
||||
tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
|
||||
@@ -734,10 +759,11 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
|
||||
LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
|
||||
(flags & (TCP_SYN | TCP_FIN)) != 0);
|
||||
|
||||
/* check for configured max queuelen and possible overflow */
|
||||
if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
|
||||
pcb->snd_queuelen, TCP_SND_QUEUELEN));
|
||||
/* check for configured max queuelen and possible overflow (FIN flag should always come through!) */
|
||||
if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) &&
|
||||
((flags & TCP_FIN) == 0)) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
|
||||
pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
|
||||
TCP_STATS_INC(tcp.memerr);
|
||||
pcb->flags |= TF_NAGLEMEMERR;
|
||||
return ERR_MEM;
|
||||
@@ -745,24 +771,23 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
|
||||
|
||||
if (flags & TCP_SYN) {
|
||||
optflags = TF_SEG_OPTS_MSS;
|
||||
#if LWIP_WND_SCALE
|
||||
if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_WND_SCALE)) {
|
||||
/* In a <SYN,ACK> (sent in state SYN_RCVD), the window scale option may only
|
||||
be sent if we received a window scale option from the remote host. */
|
||||
optflags |= TF_SEG_OPTS_WND_SCALE;
|
||||
}
|
||||
#endif /* LWIP_WND_SCALE */
|
||||
}
|
||||
#if LWIP_TCP_TIMESTAMPS
|
||||
if ((pcb->flags & TF_TIMESTAMP)) {
|
||||
/* Make sure the timestamp option is only included in data segments if we
|
||||
agreed about it with the remote host. */
|
||||
optflags |= TF_SEG_OPTS_TS;
|
||||
}
|
||||
#endif /* LWIP_TCP_TIMESTAMPS */
|
||||
optlen = LWIP_TCP_OPT_LENGTH(optflags);
|
||||
|
||||
/* tcp_enqueue_flags is always called with either SYN or FIN in flags.
|
||||
* We need one available snd_buf byte to do that.
|
||||
* This means we can't send FIN while snd_buf==0. A better fix would be to
|
||||
* not include SYN and FIN sequence numbers in the snd_buf count. */
|
||||
if (pcb->snd_buf == 0) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
|
||||
TCP_STATS_INC(tcp.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Allocate pbuf with room for TCP header + options */
|
||||
if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
|
||||
pcb->flags |= TF_NAGLEMEMERR;
|
||||
@@ -778,7 +803,7 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
|
||||
TCP_STATS_INC(tcp.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
|
||||
LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % LWIP_MIN(MEM_ALIGNMENT, 4)) == 0);
|
||||
LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
|
||||
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
|
||||
@@ -804,7 +829,6 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
|
||||
if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
|
||||
pcb->snd_lbb++;
|
||||
/* optlen does not influence snd_buf */
|
||||
pcb->snd_buf--;
|
||||
}
|
||||
if (flags & TCP_FIN) {
|
||||
pcb->flags |= TF_FIN;
|
||||
@@ -837,6 +861,19 @@ tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_WND_SCALE
|
||||
/** Build a window scale option (3 bytes long) at the specified options pointer)
|
||||
*
|
||||
* @param opts option pointer where to store the window scale option
|
||||
*/
|
||||
static void
|
||||
tcp_build_wnd_scale_option(u32_t *opts)
|
||||
{
|
||||
/* Pad with one NOP option to make everything nicely aligned */
|
||||
opts[0] = PP_HTONL(0x01030300 | TCP_RCV_SCALE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Send an ACK without data.
|
||||
*
|
||||
* @param pcb Protocol control block for the TCP connection to send the ACK
|
||||
@@ -844,9 +881,13 @@ tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
|
||||
err_t
|
||||
tcp_send_empty_ack(struct tcp_pcb *pcb)
|
||||
{
|
||||
err_t err;
|
||||
struct pbuf *p;
|
||||
struct tcp_hdr *tcphdr;
|
||||
u8_t optlen = 0;
|
||||
struct netif *netif;
|
||||
#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
|
||||
struct tcp_hdr *tcphdr;
|
||||
#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
|
||||
|
||||
#if LWIP_TCP_TIMESTAMPS
|
||||
if (pcb->flags & TF_TIMESTAMP) {
|
||||
@@ -856,16 +897,18 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
|
||||
|
||||
p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
|
||||
if (p == NULL) {
|
||||
/* let tcp_fasttmr retry sending this ACK */
|
||||
pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
|
||||
return ERR_BUF;
|
||||
}
|
||||
#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
|
||||
tcphdr = (struct tcp_hdr *)p->payload;
|
||||
#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
|
||||
("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
|
||||
/* remove ACK flags from the PCB, as we send an empty ACK now */
|
||||
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
|
||||
|
||||
/* NB. MSS option is only sent on SYNs, so ignore it here */
|
||||
/* NB. MSS and window scale options are only sent on SYNs, so ignore them here */
|
||||
#if LWIP_TCP_TIMESTAMPS
|
||||
pcb->ts_lastacksent = pcb->rcv_nxt;
|
||||
|
||||
@@ -874,20 +917,32 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
|
||||
}
|
||||
#endif
|
||||
|
||||
netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
|
||||
if (netif == NULL) {
|
||||
err = ERR_RTE;
|
||||
} else {
|
||||
#if CHECKSUM_GEN_TCP
|
||||
tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
|
||||
IP_PROTO_TCP, p->tot_len);
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
|
||||
tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
|
||||
&pcb->local_ip, &pcb->remote_ip);
|
||||
}
|
||||
#endif
|
||||
#if LWIP_NETIF_HWADDRHINT
|
||||
ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
|
||||
IP_PROTO_TCP, &(pcb->addr_hint));
|
||||
#else /* LWIP_NETIF_HWADDRHINT*/
|
||||
ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
|
||||
IP_PROTO_TCP);
|
||||
#endif /* LWIP_NETIF_HWADDRHINT*/
|
||||
NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
|
||||
err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip,
|
||||
pcb->ttl, pcb->tos, IP_PROTO_TCP, netif);
|
||||
NETIF_SET_HWADDRHINT(netif, NULL);
|
||||
}
|
||||
pbuf_free(p);
|
||||
|
||||
return ERR_OK;
|
||||
if (err != ERR_OK) {
|
||||
/* let tcp_fasttmr retry sending this ACK */
|
||||
pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
|
||||
} else {
|
||||
/* remove ACK flags from the PCB, as we sent an empty ACK now */
|
||||
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -902,6 +957,8 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
{
|
||||
struct tcp_seg *seg, *useg;
|
||||
u32_t wnd, snd_nxt;
|
||||
err_t err;
|
||||
struct netif *netif;
|
||||
#if TCP_CWND_DEBUG
|
||||
s16_t i = 0;
|
||||
#endif /* TCP_CWND_DEBUG */
|
||||
@@ -940,6 +997,20 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
for (; useg->next != NULL; useg = useg->next);
|
||||
}
|
||||
|
||||
netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
|
||||
if (netif == NULL) {
|
||||
return ERR_RTE;
|
||||
}
|
||||
|
||||
/* If we don't have a local IP address, we get one from netif */
|
||||
if (ip_addr_isany(&pcb->local_ip)) {
|
||||
const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, &pcb->remote_ip);
|
||||
if (local_ip == NULL) {
|
||||
return ERR_RTE;
|
||||
}
|
||||
ip_addr_copy(pcb->local_ip, *local_ip);
|
||||
}
|
||||
|
||||
#if TCP_OUTPUT_DEBUG
|
||||
if (seg == NULL) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
|
||||
@@ -948,13 +1019,13 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
#endif /* TCP_OUTPUT_DEBUG */
|
||||
#if TCP_CWND_DEBUG
|
||||
if (seg == NULL) {
|
||||
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
|
||||
", cwnd %"U16_F", wnd %"U32_F
|
||||
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F
|
||||
", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
|
||||
", seg == NULL, ack %"U32_F"\n",
|
||||
pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
|
||||
} else {
|
||||
LWIP_DEBUGF(TCP_CWND_DEBUG,
|
||||
("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
|
||||
("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
|
||||
", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
|
||||
pcb->snd_wnd, pcb->cwnd, wnd,
|
||||
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
|
||||
@@ -978,7 +1049,7 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
break;
|
||||
}
|
||||
#if TCP_CWND_DEBUG
|
||||
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
|
||||
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
|
||||
pcb->snd_wnd, pcb->cwnd, wnd,
|
||||
ntohl(seg->tcphdr->seqno) + seg->len -
|
||||
pcb->lastack,
|
||||
@@ -986,14 +1057,23 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
++i;
|
||||
#endif /* TCP_CWND_DEBUG */
|
||||
|
||||
pcb->unsent = seg->next;
|
||||
|
||||
if (pcb->state != SYN_SENT) {
|
||||
TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
|
||||
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
|
||||
}
|
||||
|
||||
tcp_output_segment(seg, pcb);
|
||||
#if TCP_OVERSIZE_DBGCHECK
|
||||
seg->oversize_left = 0;
|
||||
#endif /* TCP_OVERSIZE_DBGCHECK */
|
||||
err = tcp_output_segment(seg, pcb, netif);
|
||||
if (err != ERR_OK) {
|
||||
/* segment could not be sent, for whatever reason */
|
||||
pcb->flags |= TF_NAGLEMEMERR;
|
||||
return err;
|
||||
}
|
||||
pcb->unsent = seg->next;
|
||||
if (pcb->state != SYN_SENT) {
|
||||
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
|
||||
}
|
||||
snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
|
||||
if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
|
||||
pcb->snd_nxt = snd_nxt;
|
||||
@@ -1047,33 +1127,48 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
*
|
||||
* @param seg the tcp_seg to send
|
||||
* @param pcb the tcp_pcb for the TCP connection used to send the segment
|
||||
* @param netif the netif used to send the segment
|
||||
*/
|
||||
static void
|
||||
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
static err_t
|
||||
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif)
|
||||
{
|
||||
err_t err;
|
||||
u16_t len;
|
||||
struct netif *netif;
|
||||
u32_t *opts;
|
||||
|
||||
/** @bug Exclude retransmitted segments from this count. */
|
||||
snmp_inc_tcpoutsegs();
|
||||
if (seg->p->ref != 1) {
|
||||
/* This can happen if the pbuf of this segment is still referenced by the
|
||||
netif driver due to deferred transmission. Since this function modifies
|
||||
p->len, we must not continue in this case. */
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* The TCP header has already been constructed, but the ackno and
|
||||
wnd fields remain. */
|
||||
seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
|
||||
|
||||
/* advertise our receive window size in this TCP segment */
|
||||
seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
|
||||
#if LWIP_WND_SCALE
|
||||
if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
|
||||
/* The Window field in a SYN segment itself (the only type where we send
|
||||
the window scale option) is never scaled. */
|
||||
seg->tcphdr->wnd = htons(TCPWND_MIN16(pcb->rcv_ann_wnd));
|
||||
} else
|
||||
#endif /* LWIP_WND_SCALE */
|
||||
{
|
||||
seg->tcphdr->wnd = htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
|
||||
}
|
||||
|
||||
pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
|
||||
|
||||
/* Add any requested options. NB MSS option is only set on SYN
|
||||
packets, so ignore it here */
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
opts = (u32_t *)(void *)(seg->tcphdr + 1);
|
||||
if (seg->flags & TF_SEG_OPTS_MSS) {
|
||||
u16_t mss;
|
||||
#if TCP_CALCULATE_EFF_SEND_MSS
|
||||
mss = tcp_eff_send_mss(TCP_MSS, &pcb->remote_ip);
|
||||
mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip);
|
||||
#else /* TCP_CALCULATE_EFF_SEND_MSS */
|
||||
mss = TCP_MSS;
|
||||
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
|
||||
@@ -1088,23 +1183,19 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
opts += 3;
|
||||
}
|
||||
#endif
|
||||
#if LWIP_WND_SCALE
|
||||
if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
|
||||
tcp_build_wnd_scale_option(opts);
|
||||
opts += 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set retransmission timer running if it is not currently enabled
|
||||
This must be set before checking the route. */
|
||||
if (pcb->rtime == -1) {
|
||||
if (pcb->rtime < 0) {
|
||||
pcb->rtime = 0;
|
||||
}
|
||||
|
||||
/* If we don't have a local IP address, we get one by
|
||||
calling ip_route(). */
|
||||
if (ip_addr_isany(&(pcb->local_ip))) {
|
||||
netif = ip_route(&(pcb->remote_ip));
|
||||
if (netif == NULL) {
|
||||
return;
|
||||
}
|
||||
ip_addr_copy(pcb->local_ip, netif->ip_addr);
|
||||
}
|
||||
|
||||
if (pcb->rttest == 0) {
|
||||
pcb->rttest = tcp_ticks;
|
||||
pcb->rtseq = ntohl(seg->tcphdr->seqno);
|
||||
@@ -1116,6 +1207,10 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
seg->len));
|
||||
|
||||
len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
|
||||
if (len == 0) {
|
||||
/** Exclude retransmitted segments from this count. */
|
||||
MIB2_STATS_INC(mib2.tcpoutsegs);
|
||||
}
|
||||
|
||||
seg->p->len -= len;
|
||||
seg->p->tot_len -= len;
|
||||
@@ -1124,13 +1219,12 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
|
||||
seg->tcphdr->chksum = 0;
|
||||
#if CHECKSUM_GEN_TCP
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
|
||||
#if TCP_CHECKSUM_ON_COPY
|
||||
{
|
||||
u32_t acc;
|
||||
#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
|
||||
u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
|
||||
&(pcb->remote_ip),
|
||||
IP_PROTO_TCP, seg->p->tot_len);
|
||||
u16_t chksum_slow = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
|
||||
seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
|
||||
#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
|
||||
if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
|
||||
LWIP_ASSERT("data included but not checksummed",
|
||||
@@ -1138,9 +1232,8 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
}
|
||||
|
||||
/* rebuild TCP header checksum (TCP header changes for retransmissions!) */
|
||||
acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip),
|
||||
&(pcb->remote_ip),
|
||||
IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4);
|
||||
acc = ip_chksum_pseudo_partial(seg->p, IP_PROTO_TCP,
|
||||
seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip);
|
||||
/* add payload checksum */
|
||||
if (seg->chksum_swapped) {
|
||||
seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
|
||||
@@ -1150,28 +1243,25 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
seg->tcphdr->chksum = FOLD_U32T(acc);
|
||||
#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
|
||||
if (chksum_slow != seg->tcphdr->chksum) {
|
||||
LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
|
||||
TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(
|
||||
("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
|
||||
seg->tcphdr->chksum, chksum_slow));
|
||||
seg->tcphdr->chksum = chksum_slow;
|
||||
}
|
||||
#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
|
||||
}
|
||||
#else /* TCP_CHECKSUM_ON_COPY */
|
||||
seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
|
||||
&(pcb->remote_ip),
|
||||
IP_PROTO_TCP, seg->p->tot_len);
|
||||
seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
|
||||
seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
|
||||
#endif /* TCP_CHECKSUM_ON_COPY */
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_TCP */
|
||||
TCP_STATS_INC(tcp.xmit);
|
||||
|
||||
#if LWIP_NETIF_HWADDRHINT
|
||||
ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
|
||||
IP_PROTO_TCP, &(pcb->addr_hint));
|
||||
#else /* LWIP_NETIF_HWADDRHINT*/
|
||||
ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
|
||||
IP_PROTO_TCP);
|
||||
#endif /* LWIP_NETIF_HWADDRHINT*/
|
||||
NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
|
||||
err = ip_output_if(seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
|
||||
pcb->tos, IP_PROTO_TCP, netif);
|
||||
NETIF_SET_HWADDRHINT(netif, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1196,11 +1286,12 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
*/
|
||||
void
|
||||
tcp_rst(u32_t seqno, u32_t ackno,
|
||||
ip_addr_t *local_ip, ip_addr_t *remote_ip,
|
||||
const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
|
||||
u16_t local_port, u16_t remote_port)
|
||||
{
|
||||
struct pbuf *p;
|
||||
struct tcp_hdr *tcphdr;
|
||||
struct netif *netif;
|
||||
p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
|
||||
if (p == NULL) {
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
|
||||
@@ -1215,18 +1306,28 @@ tcp_rst(u32_t seqno, u32_t ackno,
|
||||
tcphdr->seqno = htonl(seqno);
|
||||
tcphdr->ackno = htonl(ackno);
|
||||
TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
|
||||
#if LWIP_WND_SCALE
|
||||
tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF));
|
||||
#else
|
||||
tcphdr->wnd = PP_HTONS(TCP_WND);
|
||||
#endif
|
||||
tcphdr->chksum = 0;
|
||||
tcphdr->urgp = 0;
|
||||
|
||||
#if CHECKSUM_GEN_TCP
|
||||
tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
|
||||
IP_PROTO_TCP, p->tot_len);
|
||||
#endif
|
||||
TCP_STATS_INC(tcp.xmit);
|
||||
snmp_inc_tcpoutrsts();
|
||||
/* Send output with hardcoded TTL since we have no access to the pcb */
|
||||
ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
|
||||
MIB2_STATS_INC(mib2.tcpoutrsts);
|
||||
|
||||
netif = ip_route(local_ip, remote_ip);
|
||||
if (netif != NULL) {
|
||||
#if CHECKSUM_GEN_TCP
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
|
||||
tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
|
||||
local_ip, remote_ip);
|
||||
}
|
||||
#endif
|
||||
/* Send output with hardcoded TTL/HL since we have no access to the pcb */
|
||||
ip_output_if(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP, netif);
|
||||
}
|
||||
pbuf_free(p);
|
||||
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
|
||||
}
|
||||
@@ -1251,11 +1352,16 @@ tcp_rexmit_rto(struct tcp_pcb *pcb)
|
||||
for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
|
||||
/* concatenate unsent queue after unacked queue */
|
||||
seg->next = pcb->unsent;
|
||||
#if TCP_OVERSIZE_DBGCHECK
|
||||
/* if last unsent changed, we need to update unsent_oversize */
|
||||
if (pcb->unsent == NULL) {
|
||||
pcb->unsent_oversize = seg->oversize_left;
|
||||
}
|
||||
#endif /* TCP_OVERSIZE_DBGCHECK */
|
||||
/* unsent queue is the concatenated queue (of unacked, unsent) */
|
||||
pcb->unsent = pcb->unacked;
|
||||
/* unacked queue is now empty */
|
||||
pcb->unacked = NULL;
|
||||
/* last unsent hasn't changed, no need to reset unsent_oversize */
|
||||
|
||||
/* increment number of retransmissions */
|
||||
++pcb->nrtx;
|
||||
@@ -1309,7 +1415,7 @@ tcp_rexmit(struct tcp_pcb *pcb)
|
||||
pcb->rttest = 0;
|
||||
|
||||
/* Do the actual retransmission. */
|
||||
snmp_inc_tcpretranssegs();
|
||||
MIB2_STATS_INC(mib2.tcpretranssegs);
|
||||
/* No need to call tcp_output: we are always called from tcp_input()
|
||||
and thus tcp_output directly returns. */
|
||||
}
|
||||
@@ -1341,16 +1447,19 @@ tcp_rexmit_fast(struct tcp_pcb *pcb)
|
||||
}
|
||||
|
||||
/* The minimum value for ssthresh should be 2 MSS */
|
||||
if (pcb->ssthresh < 2*pcb->mss) {
|
||||
if (pcb->ssthresh < (2U * pcb->mss)) {
|
||||
LWIP_DEBUGF(TCP_FR_DEBUG,
|
||||
("tcp_receive: The minimum value for ssthresh %"U16_F
|
||||
("tcp_receive: The minimum value for ssthresh %"TCPWNDSIZE_F
|
||||
" should be min 2 mss %"U16_F"...\n",
|
||||
pcb->ssthresh, 2*pcb->mss));
|
||||
pcb->ssthresh, (u16_t)(2*pcb->mss)));
|
||||
pcb->ssthresh = 2*pcb->mss;
|
||||
}
|
||||
|
||||
pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
|
||||
pcb->flags |= TF_INFR;
|
||||
|
||||
/* Reset the retransmission timer to prevent immediate rto retransmissions */
|
||||
pcb->rtime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1363,45 +1472,49 @@ tcp_rexmit_fast(struct tcp_pcb *pcb)
|
||||
*
|
||||
* @param pcb the tcp_pcb for which to send a keepalive packet
|
||||
*/
|
||||
void
|
||||
err_t
|
||||
tcp_keepalive(struct tcp_pcb *pcb)
|
||||
{
|
||||
err_t err;
|
||||
struct pbuf *p;
|
||||
struct tcp_hdr *tcphdr;
|
||||
struct netif *netif;
|
||||
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
|
||||
ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to "));
|
||||
ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
|
||||
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
|
||||
tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
|
||||
tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
|
||||
|
||||
p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
|
||||
if (p == NULL) {
|
||||
LWIP_DEBUGF(TCP_DEBUG,
|
||||
("tcp_keepalive: could not allocate memory for pbuf\n"));
|
||||
return;
|
||||
return ERR_MEM;
|
||||
}
|
||||
tcphdr = (struct tcp_hdr *)p->payload;
|
||||
|
||||
netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
|
||||
if (netif == NULL) {
|
||||
err = ERR_RTE;
|
||||
} else {
|
||||
#if CHECKSUM_GEN_TCP
|
||||
tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
|
||||
IP_PROTO_TCP, p->tot_len);
|
||||
#endif
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
|
||||
struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
|
||||
tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
|
||||
&pcb->local_ip, &pcb->remote_ip);
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_TCP */
|
||||
TCP_STATS_INC(tcp.xmit);
|
||||
|
||||
/* Send output to IP */
|
||||
#if LWIP_NETIF_HWADDRHINT
|
||||
ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
|
||||
&(pcb->addr_hint));
|
||||
#else /* LWIP_NETIF_HWADDRHINT*/
|
||||
ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
|
||||
#endif /* LWIP_NETIF_HWADDRHINT*/
|
||||
|
||||
NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
|
||||
err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, netif);
|
||||
NETIF_SET_HWADDRHINT(netif, NULL);
|
||||
}
|
||||
pbuf_free(p);
|
||||
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
|
||||
pcb->snd_nxt - 1, pcb->rcv_nxt));
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
|
||||
pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -1413,25 +1526,25 @@ tcp_keepalive(struct tcp_pcb *pcb)
|
||||
*
|
||||
* @param pcb the tcp_pcb for which to send a zero-window probe packet
|
||||
*/
|
||||
void
|
||||
err_t
|
||||
tcp_zero_window_probe(struct tcp_pcb *pcb)
|
||||
{
|
||||
err_t err;
|
||||
struct pbuf *p;
|
||||
struct tcp_hdr *tcphdr;
|
||||
struct tcp_seg *seg;
|
||||
u16_t len;
|
||||
u8_t is_fin;
|
||||
struct netif *netif;
|
||||
|
||||
LWIP_DEBUGF(TCP_DEBUG,
|
||||
("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
|
||||
U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
|
||||
ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to "));
|
||||
ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
|
||||
|
||||
LWIP_DEBUGF(TCP_DEBUG,
|
||||
("tcp_zero_window_probe: tcp_ticks %"U32_F
|
||||
" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
|
||||
tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
|
||||
tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
|
||||
|
||||
seg = pcb->unacked;
|
||||
|
||||
@@ -1439,7 +1552,8 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
|
||||
seg = pcb->unsent;
|
||||
}
|
||||
if (seg == NULL) {
|
||||
return;
|
||||
/* nothing to send, zero window probe not needed */
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
|
||||
@@ -1449,7 +1563,7 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
|
||||
p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
|
||||
if (p == NULL) {
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
|
||||
return;
|
||||
return ERR_MEM;
|
||||
}
|
||||
tcphdr = (struct tcp_hdr *)p->payload;
|
||||
|
||||
@@ -1465,24 +1579,30 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
|
||||
pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len);
|
||||
}
|
||||
|
||||
netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
|
||||
if (netif == NULL) {
|
||||
err = ERR_RTE;
|
||||
} else {
|
||||
#if CHECKSUM_GEN_TCP
|
||||
tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
|
||||
IP_PROTO_TCP, p->tot_len);
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
|
||||
tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
|
||||
&pcb->local_ip, &pcb->remote_ip);
|
||||
}
|
||||
#endif
|
||||
TCP_STATS_INC(tcp.xmit);
|
||||
|
||||
/* Send output to IP */
|
||||
#if LWIP_NETIF_HWADDRHINT
|
||||
ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
|
||||
&(pcb->addr_hint));
|
||||
#else /* LWIP_NETIF_HWADDRHINT*/
|
||||
ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
|
||||
#endif /* LWIP_NETIF_HWADDRHINT*/
|
||||
NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
|
||||
err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
|
||||
0, IP_PROTO_TCP, netif);
|
||||
NETIF_SET_HWADDRHINT(netif, NULL);
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
|
||||
" ackno %"U32_F".\n",
|
||||
pcb->snd_nxt - 1, pcb->rcv_nxt));
|
||||
" ackno %"U32_F" err %d.\n",
|
||||
pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_TCP */
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user