Browse Source
- Replace uIP (src/uip/) with adapted lwIP 2.1.x TCP module - Remove original src/lwip/ (replaced by src/lwip_tcp/) - New src/lwip_tcp/ module: lwip_pbuf, lwip_tcp core/in/out - Full TCP state machine, sliding window, congestion control - Fast retransmit/recovery, Van Jacobson RTT estimation - Nagle algorithm (TF_NODELAY), delayed ACK - OOSEQ reassembly, TIME-WAIT handling - Memory: memory_pool for PCB, listen PCB, tcp_seg - Timers: uasync periodic timer drives fast/slow timers - Output: callback-based IP packet output (TUN/raw-fd) - Error codes: LERR_ prefix to avoid conflicts - pbuf: simplified PBUF_RAM with proper header space reservation - Integrate into tcp_proxy.c/h with per-pcb callbacks - Active connection API: struct proxy_conn* instead of int conn_idx - Makefile: remove uip, add lwip_tcp files - test_tcp_proxy updated for new API; test_tcp_proxy_remote disabled - Known: test_tcp_proxy data transfer needs flow tuning (WIP)congestion
57 changed files with 4310 additions and 38876 deletions
@ -1,717 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* @defgroup altcp Application layered TCP Functions |
||||
* @ingroup altcp_api |
||||
* |
||||
* This file contains the common functions for altcp to work. |
||||
* For more details see @ref altcp_api. |
||||
*/ |
||||
|
||||
/**
|
||||
* @defgroup altcp_api Application layered TCP Introduction |
||||
* @ingroup callbackstyle_api |
||||
* |
||||
* Overview |
||||
* -------- |
||||
* altcp (application layered TCP connection API; to be used from TCPIP thread) |
||||
* is an abstraction layer that prevents applications linking hard against the |
||||
* @ref tcp.h functions while providing the same functionality. It is used to |
||||
* e.g. add SSL/TLS (see LWIP_ALTCP_TLS) or proxy-connect support to an application |
||||
* written for the tcp callback API without that application knowing the |
||||
* protocol details. |
||||
* |
||||
* * This interface mimics the tcp callback API to the application while preventing |
||||
* direct linking (much like virtual functions). |
||||
* * This way, an application can make use of other application layer protocols |
||||
* on top of TCP without knowing the details (e.g. TLS, proxy connection). |
||||
* * This is achieved by simply including "lwip/altcp.h" instead of "lwip/tcp.h", |
||||
* replacing "struct tcp_pcb" with "struct altcp_pcb" and prefixing all functions |
||||
* with "altcp_" instead of "tcp_". |
||||
* |
||||
* With altcp support disabled (LWIP_ALTCP==0), applications written against the |
||||
* altcp API can still be compiled but are directly linked against the tcp.h |
||||
* callback API and then cannot use layered protocols. To minimize code changes |
||||
* in this case, the use of altcp_allocators is strongly suggested. |
||||
* |
||||
* Usage |
||||
* ----- |
||||
* To make use of this API from an existing tcp raw API application: |
||||
* * Include "lwip/altcp.h" instead of "lwip/tcp.h" |
||||
* * Replace "struct tcp_pcb" with "struct altcp_pcb" |
||||
* * Prefix all called tcp API functions with "altcp_" instead of "tcp_" to link |
||||
* against the altcp functions |
||||
* * @ref altcp_new (and @ref altcp_new_ip_type / @ref altcp_new_ip6) take |
||||
* an @ref altcp_allocator_t as an argument, whereas the original tcp API |
||||
* functions take no arguments. |
||||
* * An @ref altcp_allocator_t allocator is an object that holds a pointer to an |
||||
* allocator object and a corresponding state (e.g. for TLS, the corresponding |
||||
* state may hold certificates or keys). This way, the application does not |
||||
* even need to know if it uses TLS or pure TCP, this is handled at runtime |
||||
* by passing a specific allocator. |
||||
* * An application can alternatively bind hard to the altcp_tls API by calling |
||||
* @ref altcp_tls_new or @ref altcp_tls_wrap. |
||||
* * The TLS layer is not directly implemented by lwIP, but a port to mbedTLS is |
||||
* provided. |
||||
* * Another altcp layer is proxy-connect to use TLS behind a HTTP proxy (see |
||||
* @ref altcp_proxyconnect.h) |
||||
* |
||||
* altcp_allocator_t |
||||
* ----------------- |
||||
* An altcp allocator is created by the application by combining an allocator |
||||
* callback function and a corresponding state, e.g.:\code{.c} |
||||
* static const unsigned char cert[] = {0x2D, ... (see mbedTLS doc for how to create this)}; |
||||
* struct altcp_tls_config * conf = altcp_tls_create_config_client(cert, sizeof(cert)); |
||||
* altcp_allocator_t tls_allocator = { |
||||
* altcp_tls_alloc, conf |
||||
* }; |
||||
* \endcode |
||||
* |
||||
* |
||||
* struct altcp_tls_config |
||||
* ----------------------- |
||||
* The struct altcp_tls_config holds state that is needed to create new TLS client |
||||
* or server connections (e.g. certificates and private keys). |
||||
* |
||||
* It is not defined by lwIP itself but by the TLS port (e.g. altcp_tls to mbedTLS |
||||
* adaption). However, the parameters used to create it are defined in @ref |
||||
* altcp_tls.h (see @ref altcp_tls_create_config_server_privkey_cert for servers |
||||
* and @ref altcp_tls_create_config_client / @ref altcp_tls_create_config_client_2wayauth |
||||
* for clients). |
||||
* |
||||
* For mbedTLS, ensure that certificates can be parsed by 'mbedtls_x509_crt_parse()' and |
||||
* private keys can be parsed by 'mbedtls_pk_parse_key()'. |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2017 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 <goldsimon@gmx.de> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/altcp.h" |
||||
#include "lwip/priv/altcp_priv.h" |
||||
#include "lwip/altcp_tcp.h" |
||||
#include "lwip/tcp.h" |
||||
#include "lwip/mem.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
extern const struct altcp_functions altcp_tcp_functions; |
||||
|
||||
/**
|
||||
* For altcp layer implementations only: allocate a new struct altcp_pcb from the pool |
||||
* and zero the memory |
||||
*/ |
||||
struct altcp_pcb * |
||||
altcp_alloc(void) |
||||
{ |
||||
struct altcp_pcb *ret = (struct altcp_pcb *)memp_malloc(MEMP_ALTCP_PCB); |
||||
if (ret != NULL) { |
||||
memset(ret, 0, sizeof(struct altcp_pcb)); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* For altcp layer implementations only: return a struct altcp_pcb to the pool |
||||
*/ |
||||
void |
||||
altcp_free(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn) { |
||||
if (conn->fns && conn->fns->dealloc) { |
||||
conn->fns->dealloc(conn); |
||||
} |
||||
memp_free(MEMP_ALTCP_PCB, conn); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* altcp_new_ip6: @ref altcp_new for IPv6 |
||||
*/ |
||||
struct altcp_pcb * |
||||
altcp_new_ip6(altcp_allocator_t *allocator) |
||||
{ |
||||
return altcp_new_ip_type(allocator, IPADDR_TYPE_V6); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* altcp_new: @ref altcp_new for IPv4 |
||||
*/ |
||||
struct altcp_pcb * |
||||
altcp_new(altcp_allocator_t *allocator) |
||||
{ |
||||
return altcp_new_ip_type(allocator, IPADDR_TYPE_V4); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* altcp_new_ip_type: called by applications to allocate a new pcb with the help of an |
||||
* allocator function. |
||||
* |
||||
* @param allocator allocator function and argument |
||||
* @param ip_type IP version of the pcb (@ref lwip_ip_addr_type) |
||||
* @return a new altcp_pcb or NULL on error |
||||
*/ |
||||
struct altcp_pcb * |
||||
altcp_new_ip_type(altcp_allocator_t *allocator, u8_t ip_type) |
||||
{ |
||||
struct altcp_pcb *conn; |
||||
if (allocator == NULL) { |
||||
/* no allocator given, create a simple TCP connection */ |
||||
return altcp_tcp_new_ip_type(ip_type); |
||||
} |
||||
if (allocator->alloc == NULL) { |
||||
/* illegal allocator */ |
||||
return NULL; |
||||
} |
||||
conn = allocator->alloc(allocator->arg, ip_type); |
||||
if (conn == NULL) { |
||||
/* allocation failed */ |
||||
return NULL; |
||||
} |
||||
return conn; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_arg() |
||||
*/ |
||||
void |
||||
altcp_arg(struct altcp_pcb *conn, void *arg) |
||||
{ |
||||
if (conn) { |
||||
conn->arg = arg; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_accept() |
||||
*/ |
||||
void |
||||
altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept) |
||||
{ |
||||
if (conn != NULL) { |
||||
conn->accept = accept; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_recv() |
||||
*/ |
||||
void |
||||
altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv) |
||||
{ |
||||
if (conn) { |
||||
conn->recv = recv; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_sent() |
||||
*/ |
||||
void |
||||
altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent) |
||||
{ |
||||
if (conn) { |
||||
conn->sent = sent; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_poll() |
||||
*/ |
||||
void |
||||
altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval) |
||||
{ |
||||
if (conn) { |
||||
conn->poll = poll; |
||||
conn->pollinterval = interval; |
||||
if (conn->fns && conn->fns->set_poll) { |
||||
conn->fns->set_poll(conn, interval); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_err() |
||||
*/ |
||||
void |
||||
altcp_err(struct altcp_pcb *conn, altcp_err_fn err) |
||||
{ |
||||
if (conn) { |
||||
conn->err = err; |
||||
} |
||||
} |
||||
|
||||
/* Generic functions calling the "virtual" ones */ |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_recved() |
||||
*/ |
||||
void |
||||
altcp_recved(struct altcp_pcb *conn, u16_t len) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->recved) { |
||||
conn->fns->recved(conn, len); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_bind() |
||||
*/ |
||||
err_t |
||||
altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->bind) { |
||||
return conn->fns->bind(conn, ipaddr, port); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_connect() |
||||
*/ |
||||
err_t |
||||
altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->connect) { |
||||
return conn->fns->connect(conn, ipaddr, port, connected); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_listen_with_backlog_and_err() |
||||
*/ |
||||
struct altcp_pcb * |
||||
altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->listen) { |
||||
return conn->fns->listen(conn, backlog, err); |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_abort() |
||||
*/ |
||||
void |
||||
altcp_abort(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->abort) { |
||||
conn->fns->abort(conn); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_close() |
||||
*/ |
||||
err_t |
||||
altcp_close(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->close) { |
||||
return conn->fns->close(conn); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_shutdown() |
||||
*/ |
||||
err_t |
||||
altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->shutdown) { |
||||
return conn->fns->shutdown(conn, shut_rx, shut_tx); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_write() |
||||
*/ |
||||
err_t |
||||
altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->write) { |
||||
return conn->fns->write(conn, dataptr, len, apiflags); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_output() |
||||
*/ |
||||
err_t |
||||
altcp_output(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->output) { |
||||
return conn->fns->output(conn); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_mss() |
||||
*/ |
||||
u16_t |
||||
altcp_mss(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->mss) { |
||||
return conn->fns->mss(conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_sndbuf() |
||||
*/ |
||||
u16_t |
||||
altcp_sndbuf(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->sndbuf) { |
||||
return conn->fns->sndbuf(conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_sndqueuelen() |
||||
*/ |
||||
u16_t |
||||
altcp_sndqueuelen(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->sndqueuelen) { |
||||
return conn->fns->sndqueuelen(conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void |
||||
altcp_nagle_disable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->nagle_disable) { |
||||
conn->fns->nagle_disable(conn); |
||||
} |
||||
} |
||||
|
||||
void |
||||
altcp_nagle_enable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->nagle_enable) { |
||||
conn->fns->nagle_enable(conn); |
||||
} |
||||
} |
||||
|
||||
int |
||||
altcp_nagle_disabled(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->nagle_disabled) { |
||||
return conn->fns->nagle_disabled(conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup altcp |
||||
* @see tcp_setprio() |
||||
*/ |
||||
void |
||||
altcp_setprio(struct altcp_pcb *conn, u8_t prio) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->setprio) { |
||||
conn->fns->setprio(conn, prio); |
||||
} |
||||
} |
||||
|
||||
err_t |
||||
altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->addrinfo) { |
||||
return conn->fns->addrinfo(conn, local, addr, port); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
ip_addr_t * |
||||
altcp_get_ip(struct altcp_pcb *conn, int local) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->getip) { |
||||
return conn->fns->getip(conn, local); |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
u16_t |
||||
altcp_get_port(struct altcp_pcb *conn, int local) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->getport) { |
||||
return conn->fns->getport(conn, local); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
#if LWIP_TCP_KEEPALIVE |
||||
void |
||||
altcp_keepalive_disable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->keepalive_disable) { |
||||
conn->fns->keepalive_disable(conn); |
||||
} |
||||
} |
||||
|
||||
void |
||||
altcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->keepalive_enable) { |
||||
conn->fns->keepalive_enable(conn, idle, intvl, count); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#ifdef LWIP_DEBUG |
||||
enum tcp_state |
||||
altcp_dbg_get_tcp_state(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->fns && conn->fns->dbg_get_tcp_state) { |
||||
return conn->fns->dbg_get_tcp_state(conn); |
||||
} |
||||
return CLOSED; |
||||
} |
||||
#endif |
||||
|
||||
/* Default implementations for the "virtual" functions */ |
||||
|
||||
void |
||||
altcp_default_set_poll(struct altcp_pcb *conn, u8_t interval) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
altcp_poll(conn->inner_conn, conn->poll, interval); |
||||
} |
||||
} |
||||
|
||||
void |
||||
altcp_default_recved(struct altcp_pcb *conn, u16_t len) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
altcp_recved(conn->inner_conn, len); |
||||
} |
||||
} |
||||
|
||||
err_t |
||||
altcp_default_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_bind(conn->inner_conn, ipaddr, port); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
err_t |
||||
altcp_default_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx) |
||||
{ |
||||
if (conn) { |
||||
if (shut_rx && shut_tx && conn->fns && conn->fns->close) { |
||||
/* default shutdown for both sides is close */ |
||||
return conn->fns->close(conn); |
||||
} |
||||
if (conn->inner_conn) { |
||||
return altcp_shutdown(conn->inner_conn, shut_rx, shut_tx); |
||||
} |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
err_t |
||||
altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_write(conn->inner_conn, dataptr, len, apiflags); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
err_t |
||||
altcp_default_output(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_output(conn->inner_conn); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
u16_t |
||||
altcp_default_mss(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_mss(conn->inner_conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
u16_t |
||||
altcp_default_sndbuf(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_sndbuf(conn->inner_conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
u16_t |
||||
altcp_default_sndqueuelen(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_sndqueuelen(conn->inner_conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void |
||||
altcp_default_nagle_disable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
altcp_nagle_disable(conn->inner_conn); |
||||
} |
||||
} |
||||
|
||||
void |
||||
altcp_default_nagle_enable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
altcp_nagle_enable(conn->inner_conn); |
||||
} |
||||
} |
||||
|
||||
int |
||||
altcp_default_nagle_disabled(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_nagle_disabled(conn->inner_conn); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void |
||||
altcp_default_setprio(struct altcp_pcb *conn, u8_t prio) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
altcp_setprio(conn->inner_conn, prio); |
||||
} |
||||
} |
||||
|
||||
void |
||||
altcp_default_dealloc(struct altcp_pcb *conn) |
||||
{ |
||||
LWIP_UNUSED_ARG(conn); |
||||
/* nothing to do */ |
||||
} |
||||
|
||||
err_t |
||||
altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_get_tcp_addrinfo(conn->inner_conn, local, addr, port); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
ip_addr_t * |
||||
altcp_default_get_ip(struct altcp_pcb *conn, int local) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_get_ip(conn->inner_conn, local); |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
u16_t |
||||
altcp_default_get_port(struct altcp_pcb *conn, int local) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_get_port(conn->inner_conn, local); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
#if LWIP_TCP_KEEPALIVE |
||||
void |
||||
altcp_default_keepalive_disable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
altcp_keepalive_disable(conn->inner_conn); |
||||
} |
||||
} |
||||
|
||||
void |
||||
altcp_default_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
altcp_keepalive_enable(conn->inner_conn, idle, intvl, count); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#ifdef LWIP_DEBUG |
||||
enum tcp_state |
||||
altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->inner_conn) { |
||||
return altcp_dbg_get_tcp_state(conn->inner_conn); |
||||
} |
||||
return CLOSED; |
||||
} |
||||
#endif |
||||
|
||||
|
||||
#endif /* LWIP_ALTCP */ |
||||
@ -1,87 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Application layered TCP connection API (to be used from TCPIP thread)<br> |
||||
* This interface mimics the tcp callback API to the application while preventing |
||||
* direct linking (much like virtual functions). |
||||
* This way, an application can make use of other application layer protocols |
||||
* on top of TCP without knowing the details (e.g. TLS, proxy connection). |
||||
* |
||||
* This file contains allocation implementation that combine several layers. |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2017 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 <goldsimon@gmx.de> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/altcp.h" |
||||
#include "lwip/altcp_tcp.h" |
||||
#include "lwip/altcp_tls.h" |
||||
#include "lwip/priv/altcp_priv.h" |
||||
#include "lwip/mem.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#if LWIP_ALTCP_TLS |
||||
|
||||
/** This standard allocator function creates an altcp pcb for
|
||||
* TLS over TCP */ |
||||
struct altcp_pcb * |
||||
altcp_tls_new(struct altcp_tls_config *config, u8_t ip_type) |
||||
{ |
||||
struct altcp_pcb *inner_conn, *ret; |
||||
LWIP_UNUSED_ARG(ip_type); |
||||
|
||||
inner_conn = altcp_tcp_new_ip_type(ip_type); |
||||
if (inner_conn == NULL) { |
||||
return NULL; |
||||
} |
||||
ret = altcp_tls_wrap(config, inner_conn); |
||||
if (ret == NULL) { |
||||
altcp_close(inner_conn); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/** This standard allocator function creates an altcp pcb for
|
||||
* TLS over TCP */ |
||||
struct altcp_pcb * |
||||
altcp_tls_alloc(void *arg, u8_t ip_type) |
||||
{ |
||||
return altcp_tls_new((struct altcp_tls_config *)arg, ip_type); |
||||
} |
||||
|
||||
#endif /* LWIP_ALTCP_TLS */ |
||||
|
||||
#endif /* LWIP_ALTCP */ |
||||
@ -1,578 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Application layered TCP connection API (to be used from TCPIP thread) |
||||
* |
||||
* This interface mimics the tcp callback API to the application while preventing |
||||
* direct linking (much like virtual functions). |
||||
* This way, an application can make use of other application layer protocols |
||||
* on top of TCP without knowing the details (e.g. TLS, proxy connection). |
||||
* |
||||
* This file contains the base implementation calling into tcp. |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2017 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 <goldsimon@gmx.de> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/altcp.h" |
||||
#include "lwip/altcp_tcp.h" |
||||
#include "lwip/priv/altcp_priv.h" |
||||
#include "lwip/tcp.h" |
||||
#include "lwip/priv/tcp_priv.h" |
||||
#include "lwip/mem.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#define ALTCP_TCP_ASSERT_CONN(conn) do { \ |
||||
LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL); \
|
||||
LWIP_UNUSED_ARG(conn); /* for LWIP_NOASSERT */ } while(0) |
||||
#define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \ |
||||
LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
|
||||
LWIP_UNUSED_ARG(tpcb); /* for LWIP_NOASSERT */ \
|
||||
ALTCP_TCP_ASSERT_CONN(conn); } while(0) |
||||
|
||||
|
||||
/* Variable prototype, the actual declaration is at the end of this file
|
||||
since it contains pointers to static functions declared here */ |
||||
extern const struct altcp_functions altcp_tcp_functions; |
||||
|
||||
static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb); |
||||
|
||||
/* callback functions for TCP */ |
||||
static err_t |
||||
altcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err) |
||||
{ |
||||
struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg; |
||||
if (new_tpcb && listen_conn && listen_conn->accept) { |
||||
/* create a new altcp_conn to pass to the next 'accept' callback */ |
||||
struct altcp_pcb *new_conn = altcp_alloc(); |
||||
if (new_conn == NULL) { |
||||
return ERR_MEM; |
||||
} |
||||
altcp_tcp_setup(new_conn, new_tpcb); |
||||
return listen_conn->accept(listen_conn->arg, new_conn, err); |
||||
} |
||||
return ERR_ARG; |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) |
||||
{ |
||||
struct altcp_pcb *conn = (struct altcp_pcb *)arg; |
||||
if (conn) { |
||||
ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb); |
||||
if (conn->connected) { |
||||
return conn->connected(conn->arg, conn, err); |
||||
} |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) |
||||
{ |
||||
struct altcp_pcb *conn = (struct altcp_pcb *)arg; |
||||
if (conn) { |
||||
ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb); |
||||
if (conn->recv) { |
||||
return conn->recv(conn->arg, conn, p, err); |
||||
} |
||||
} |
||||
if (p != NULL) { |
||||
/* prevent memory leaks */ |
||||
pbuf_free(p); |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) |
||||
{ |
||||
struct altcp_pcb *conn = (struct altcp_pcb *)arg; |
||||
if (conn) { |
||||
ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb); |
||||
if (conn->sent) { |
||||
return conn->sent(conn->arg, conn, len); |
||||
} |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_poll(void *arg, struct tcp_pcb *tpcb) |
||||
{ |
||||
struct altcp_pcb *conn = (struct altcp_pcb *)arg; |
||||
if (conn) { |
||||
ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb); |
||||
if (conn->poll) { |
||||
return conn->poll(conn->arg, conn); |
||||
} |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_err(void *arg, err_t err) |
||||
{ |
||||
struct altcp_pcb *conn = (struct altcp_pcb *)arg; |
||||
if (conn) { |
||||
conn->state = NULL; /* already freed */ |
||||
if (conn->err) { |
||||
conn->err(conn->arg, err); |
||||
} |
||||
altcp_free(conn); |
||||
} |
||||
} |
||||
|
||||
/* setup functions */ |
||||
|
||||
static void |
||||
altcp_tcp_remove_callbacks(struct tcp_pcb *tpcb) |
||||
{ |
||||
tcp_arg(tpcb, NULL); |
||||
if (tpcb->state != LISTEN) { |
||||
tcp_recv(tpcb, NULL); |
||||
tcp_sent(tpcb, NULL); |
||||
tcp_err(tpcb, NULL); |
||||
tcp_poll(tpcb, NULL, tpcb->pollinterval); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb) |
||||
{ |
||||
tcp_arg(tpcb, conn); |
||||
/* this might be called for LISTN when close fails... */ |
||||
if (tpcb->state != LISTEN) { |
||||
tcp_recv(tpcb, altcp_tcp_recv); |
||||
tcp_sent(tpcb, altcp_tcp_sent); |
||||
tcp_err(tpcb, altcp_tcp_err); |
||||
/* tcp_poll is set when interval is set by application */ |
||||
} |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb) |
||||
{ |
||||
altcp_tcp_setup_callbacks(conn, tpcb); |
||||
conn->state = tpcb; |
||||
conn->fns = &altcp_tcp_functions; |
||||
} |
||||
|
||||
struct altcp_pcb * |
||||
altcp_tcp_new_ip_type(u8_t ip_type) |
||||
{ |
||||
/* Allocate the tcp pcb first to invoke the priority handling code
|
||||
if we're out of pcbs */ |
||||
struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type); |
||||
if (tpcb != NULL) { |
||||
struct altcp_pcb *ret = altcp_alloc(); |
||||
if (ret != NULL) { |
||||
altcp_tcp_setup(ret, tpcb); |
||||
return ret; |
||||
} else { |
||||
/* altcp_pcb allocation failed -> free the tcp_pcb too */ |
||||
tcp_close(tpcb); |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
/** altcp_tcp allocator function fitting to @ref altcp_allocator_t / @ref altcp_new.
|
||||
* |
||||
* arg pointer is not used for TCP. |
||||
*/ |
||||
struct altcp_pcb * |
||||
altcp_tcp_alloc(void *arg, u8_t ip_type) |
||||
{ |
||||
LWIP_UNUSED_ARG(arg); |
||||
return altcp_tcp_new_ip_type(ip_type); |
||||
} |
||||
|
||||
struct altcp_pcb * |
||||
altcp_tcp_wrap(struct tcp_pcb *tpcb) |
||||
{ |
||||
if (tpcb != NULL) { |
||||
struct altcp_pcb *ret = altcp_alloc(); |
||||
if (ret != NULL) { |
||||
altcp_tcp_setup(ret, tpcb); |
||||
return ret; |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
|
||||
/* "virtual" functions calling into tcp */ |
||||
static void |
||||
altcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval) |
||||
{ |
||||
if (conn != NULL) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
tcp_poll(pcb, altcp_tcp_poll, interval); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_recved(struct altcp_pcb *conn, u16_t len) |
||||
{ |
||||
if (conn != NULL) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
tcp_recved(pcb, len); |
||||
} |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return ERR_VAL; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_bind(pcb, ipaddr, port); |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return ERR_VAL; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
conn->connected = connected; |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected); |
||||
} |
||||
|
||||
static struct altcp_pcb * |
||||
altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
struct tcp_pcb *lpcb; |
||||
if (conn == NULL) { |
||||
return NULL; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err); |
||||
if (lpcb != NULL) { |
||||
conn->state = lpcb; |
||||
tcp_accept(lpcb, altcp_tcp_accept); |
||||
return conn; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_abort(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn != NULL) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
if (pcb) { |
||||
tcp_abort(pcb); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_close(struct altcp_pcb *conn) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return ERR_VAL; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
if (pcb) { |
||||
err_t err; |
||||
tcp_poll_fn oldpoll = pcb->poll; |
||||
altcp_tcp_remove_callbacks(pcb); |
||||
err = tcp_close(pcb); |
||||
if (err != ERR_OK) { |
||||
/* not closed, set up all callbacks again */ |
||||
altcp_tcp_setup_callbacks(conn, pcb); |
||||
/* poll callback is not included in the above */ |
||||
tcp_poll(pcb, oldpoll, pcb->pollinterval); |
||||
return err; |
||||
} |
||||
conn->state = NULL; /* unsafe to reference pcb after tcp_close(). */ |
||||
} |
||||
altcp_free(conn); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return ERR_VAL; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_shutdown(pcb, shut_rx, shut_tx); |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return ERR_VAL; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_write(pcb, dataptr, len, apiflags); |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_output(struct altcp_pcb *conn) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return ERR_VAL; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_output(pcb); |
||||
} |
||||
|
||||
static u16_t |
||||
altcp_tcp_mss(struct altcp_pcb *conn) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return 0; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_mss(pcb); |
||||
} |
||||
|
||||
static u16_t |
||||
altcp_tcp_sndbuf(struct altcp_pcb *conn) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return 0; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_sndbuf(pcb); |
||||
} |
||||
|
||||
static u16_t |
||||
altcp_tcp_sndqueuelen(struct altcp_pcb *conn) |
||||
{ |
||||
struct tcp_pcb *pcb; |
||||
if (conn == NULL) { |
||||
return 0; |
||||
} |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
pcb = (struct tcp_pcb *)conn->state; |
||||
return tcp_sndqueuelen(pcb); |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_nagle_disable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->state) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
tcp_nagle_disable(pcb); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_nagle_enable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->state) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
tcp_nagle_enable(pcb); |
||||
} |
||||
} |
||||
|
||||
static int |
||||
altcp_tcp_nagle_disabled(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->state) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
return tcp_nagle_disabled(pcb); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio) |
||||
{ |
||||
if (conn != NULL) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
tcp_setprio(pcb, prio); |
||||
} |
||||
} |
||||
|
||||
#if LWIP_TCP_KEEPALIVE |
||||
static void |
||||
altcp_tcp_keepalive_disable(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn && conn->state) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
ip_reset_option(pcb, SOF_KEEPALIVE); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
altcp_tcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t cnt) |
||||
{ |
||||
if (conn && conn->state) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
ip_set_option(pcb, SOF_KEEPALIVE); |
||||
pcb->keep_idle = idle ? idle : TCP_KEEPIDLE_DEFAULT; |
||||
pcb->keep_intvl = intvl ? intvl : TCP_KEEPINTVL_DEFAULT; |
||||
pcb->keep_cnt = cnt ? cnt : TCP_KEEPCNT_DEFAULT; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
static void |
||||
altcp_tcp_dealloc(struct altcp_pcb *conn) |
||||
{ |
||||
LWIP_UNUSED_ARG(conn); |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
/* no private state to clean up */ |
||||
} |
||||
|
||||
static err_t |
||||
altcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port) |
||||
{ |
||||
if (conn) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
static ip_addr_t * |
||||
altcp_tcp_get_ip(struct altcp_pcb *conn, int local) |
||||
{ |
||||
if (conn) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
if (pcb) { |
||||
if (local) { |
||||
return &pcb->local_ip; |
||||
} else { |
||||
return &pcb->remote_ip; |
||||
} |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
static u16_t |
||||
altcp_tcp_get_port(struct altcp_pcb *conn, int local) |
||||
{ |
||||
if (conn) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
if (pcb) { |
||||
if (local) { |
||||
return pcb->local_port; |
||||
} else { |
||||
return pcb->remote_port; |
||||
} |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
#ifdef LWIP_DEBUG |
||||
static enum tcp_state |
||||
altcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn) |
||||
{ |
||||
if (conn) { |
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state; |
||||
ALTCP_TCP_ASSERT_CONN(conn); |
||||
if (pcb) { |
||||
return pcb->state; |
||||
} |
||||
} |
||||
return CLOSED; |
||||
} |
||||
#endif |
||||
const struct altcp_functions altcp_tcp_functions = { |
||||
altcp_tcp_set_poll, |
||||
altcp_tcp_recved, |
||||
altcp_tcp_bind, |
||||
altcp_tcp_connect, |
||||
altcp_tcp_listen, |
||||
altcp_tcp_abort, |
||||
altcp_tcp_close, |
||||
altcp_tcp_shutdown, |
||||
altcp_tcp_write, |
||||
altcp_tcp_output, |
||||
altcp_tcp_mss, |
||||
altcp_tcp_sndbuf, |
||||
altcp_tcp_sndqueuelen, |
||||
altcp_tcp_nagle_disable, |
||||
altcp_tcp_nagle_enable, |
||||
altcp_tcp_nagle_disabled, |
||||
altcp_tcp_setprio, |
||||
altcp_tcp_dealloc, |
||||
altcp_tcp_get_tcp_addrinfo, |
||||
altcp_tcp_get_ip, |
||||
altcp_tcp_get_port |
||||
#if LWIP_TCP_KEEPALIVE |
||||
, altcp_tcp_keepalive_disable |
||||
, altcp_tcp_keepalive_enable |
||||
#endif |
||||
#ifdef LWIP_DEBUG |
||||
, altcp_tcp_dbg_get_tcp_state |
||||
#endif |
||||
}; |
||||
|
||||
#endif /* LWIP_ALTCP */ |
||||
@ -1,286 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Common functions used throughout the stack. |
||||
* |
||||
* These are reference implementations of the byte swapping functions. |
||||
* Again with the aim of being simple, correct and fully portable. |
||||
* Byte swapping is the second thing you would want to optimize. You will |
||||
* need to port it to your architecture and in your cc.h: |
||||
* |
||||
* \#define lwip_htons(x) your_htons |
||||
* \#define lwip_htonl(x) your_htonl |
||||
* |
||||
* Note lwip_ntohs() and lwip_ntohl() are merely references to the htonx counterparts. |
||||
* |
||||
* If you \#define them to htons() and htonl(), you should |
||||
* \#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from |
||||
* defining htonx/ntohx compatibility macros. |
||||
|
||||
* @defgroup sys_nonstandard Non-standard functions |
||||
* @ingroup sys_layer |
||||
* lwIP provides default implementations for non-standard functions. |
||||
* These can be mapped to OS functions to reduce code footprint if desired. |
||||
* All defines related to this section must not be placed in lwipopts.h, |
||||
* but in arch/cc.h! |
||||
* These options cannot be \#defined in lwipopts.h since they are not options |
||||
* of lwIP itself, but options of the lwIP port to your system. |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met: |
||||
* |
||||
* 1. Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* 3. The name of the author may not be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||||
* OF SUCH DAMAGE. |
||||
* |
||||
* This file is part of the lwIP TCP/IP stack. |
||||
* |
||||
* Author: Simon Goldschmidt |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
#include "lwip/def.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN |
||||
|
||||
#if !defined(lwip_htons) |
||||
/**
|
||||
* Convert an u16_t from host- to network byte order. |
||||
* |
||||
* @param n u16_t in host byte order |
||||
* @return n in network byte order |
||||
*/ |
||||
u16_t |
||||
lwip_htons(u16_t n) |
||||
{ |
||||
return PP_HTONS(n); |
||||
} |
||||
#endif /* lwip_htons */ |
||||
|
||||
#if !defined(lwip_htonl) |
||||
/**
|
||||
* Convert an u32_t from host- to network byte order. |
||||
* |
||||
* @param n u32_t in host byte order |
||||
* @return n in network byte order |
||||
*/ |
||||
u32_t |
||||
lwip_htonl(u32_t n) |
||||
{ |
||||
return PP_HTONL(n); |
||||
} |
||||
#endif /* lwip_htonl */ |
||||
|
||||
#endif /* BYTE_ORDER == LITTLE_ENDIAN */ |
||||
|
||||
#ifndef lwip_strnstr |
||||
/**
|
||||
* @ingroup sys_nonstandard |
||||
* lwIP default implementation for strnstr() non-standard function. |
||||
* This can be \#defined to strnstr() depending on your platform port. |
||||
*/ |
||||
char * |
||||
lwip_strnstr(const char *buffer, const char *token, size_t n) |
||||
{ |
||||
const char *p; |
||||
size_t tokenlen = strlen(token); |
||||
if (tokenlen == 0) { |
||||
return LWIP_CONST_CAST(char *, buffer); |
||||
} |
||||
for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) { |
||||
if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) { |
||||
return LWIP_CONST_CAST(char *, p); |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
#endif |
||||
|
||||
#ifndef lwip_strnistr |
||||
/**
|
||||
* @ingroup sys_nonstandard |
||||
* lwIP default implementation for strnistr() non-standard function. |
||||
* This can be \#defined to strnistr() depending on your platform port. |
||||
*/ |
||||
char * |
||||
lwip_strnistr(const char *buffer, const char *token, size_t n) |
||||
{ |
||||
const char *p; |
||||
size_t tokenlen = strlen(token); |
||||
if (tokenlen == 0) { |
||||
return LWIP_CONST_CAST(char *, buffer); |
||||
} |
||||
for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) { |
||||
if (lwip_strnicmp(p, token, tokenlen) == 0) { |
||||
return LWIP_CONST_CAST(char *, p); |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
#endif |
||||
|
||||
#ifndef lwip_stricmp |
||||
/**
|
||||
* @ingroup sys_nonstandard |
||||
* lwIP default implementation for stricmp() non-standard function. |
||||
* This can be \#defined to stricmp() depending on your platform port. |
||||
*/ |
||||
int |
||||
lwip_stricmp(const char *str1, const char *str2) |
||||
{ |
||||
char c1, c2; |
||||
|
||||
do { |
||||
c1 = *str1++; |
||||
c2 = *str2++; |
||||
if (c1 != c2) { |
||||
char c1_upc = c1 | 0x20; |
||||
if ((c1_upc >= 'a') && (c1_upc <= 'z')) { |
||||
/* characters are not equal an one is in the alphabet range:
|
||||
downcase both chars and check again */ |
||||
char c2_upc = c2 | 0x20; |
||||
if (c1_upc != c2_upc) { |
||||
/* still not equal */ |
||||
/* don't care for < or > */ |
||||
return 1; |
||||
} |
||||
} else { |
||||
/* characters are not equal but none is in the alphabet range */ |
||||
return 1; |
||||
} |
||||
} |
||||
} while (c1 != 0); |
||||
return 0; |
||||
} |
||||
#endif |
||||
|
||||
#ifndef lwip_strnicmp |
||||
/**
|
||||
* @ingroup sys_nonstandard |
||||
* lwIP default implementation for strnicmp() non-standard function. |
||||
* This can be \#defined to strnicmp() depending on your platform port. |
||||
*/ |
||||
int |
||||
lwip_strnicmp(const char *str1, const char *str2, size_t len) |
||||
{ |
||||
char c1, c2; |
||||
|
||||
do { |
||||
c1 = *str1++; |
||||
c2 = *str2++; |
||||
if (c1 != c2) { |
||||
char c1_upc = c1 | 0x20; |
||||
if ((c1_upc >= 'a') && (c1_upc <= 'z')) { |
||||
/* characters are not equal an one is in the alphabet range:
|
||||
downcase both chars and check again */ |
||||
char c2_upc = c2 | 0x20; |
||||
if (c1_upc != c2_upc) { |
||||
/* still not equal */ |
||||
/* don't care for < or > */ |
||||
return 1; |
||||
} |
||||
} else { |
||||
/* characters are not equal but none is in the alphabet range */ |
||||
return 1; |
||||
} |
||||
} |
||||
len--; |
||||
} while ((len != 0) && (c1 != 0)); |
||||
return 0; |
||||
} |
||||
#endif |
||||
|
||||
#ifndef lwip_itoa |
||||
/**
|
||||
* @ingroup sys_nonstandard |
||||
* lwIP default implementation for itoa() non-standard function. |
||||
* This can be \#defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform port. |
||||
*/ |
||||
void |
||||
lwip_itoa(char *result, size_t bufsize, int number) |
||||
{ |
||||
char *res = result; |
||||
char *tmp = result + bufsize - 1; |
||||
int n = (number >= 0) ? number : -number; |
||||
|
||||
/* handle invalid bufsize */ |
||||
if (bufsize < 2) { |
||||
if (bufsize == 1) { |
||||
*result = 0; |
||||
} |
||||
return; |
||||
} |
||||
|
||||
/* First, add sign */ |
||||
if (number < 0) { |
||||
*res++ = '-'; |
||||
} |
||||
/* Then create the string from the end and stop if buffer full,
|
||||
and ensure output string is zero terminated */ |
||||
*tmp = 0; |
||||
while ((n != 0) && (tmp > res)) { |
||||
char val = (char)('0' + (n % 10)); |
||||
tmp--; |
||||
*tmp = val; |
||||
n = n / 10; |
||||
} |
||||
if (n) { |
||||
/* buffer is too small */ |
||||
*result = 0; |
||||
return; |
||||
} |
||||
if (*tmp == 0) { |
||||
/* Nothing added? */ |
||||
*res++ = '0'; |
||||
*res++ = 0; |
||||
return; |
||||
} |
||||
/* move from temporary buffer to output buffer (sign is not moved) */ |
||||
memmove(res, tmp, (size_t)((result + bufsize) - tmp)); |
||||
} |
||||
#endif |
||||
|
||||
#ifndef lwip_memcmp_consttime |
||||
/**
|
||||
* @ingroup sys_nonstandard |
||||
* The goal of this function is to compare memory with constant runtime in order to prevent |
||||
* timing attacks to various parts in the stack. |
||||
* To do that, in contrast to memcmp(), it only returns: |
||||
* 0: equal |
||||
* != 0: not equal |
||||
*/ |
||||
int lwip_memcmp_consttime(const void* s1, const void* s2, size_t len) |
||||
{ |
||||
size_t i; |
||||
const unsigned char* a1 = (const unsigned char*)s1; |
||||
const unsigned char* a2 = (const unsigned char*)s2; |
||||
unsigned char ret = 0; |
||||
|
||||
for (i = 0; i < len; i++) { |
||||
ret |= a1[i] ^ a2[i]; |
||||
} |
||||
return ret; |
||||
} |
||||
#endif |
||||
@ -1,608 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Internet checksum functions. |
||||
* |
||||
* These are some reference implementations of the checksum algorithm, with the |
||||
* aim of being simple, correct and fully portable. Checksumming is the |
||||
* first thing you would want to optimize for your platform. If you create |
||||
* your own version, link it in and in your cc.h put: |
||||
* |
||||
* \#define LWIP_CHKSUM your_checksum_routine |
||||
* |
||||
* Or you can select from the implementations below by defining |
||||
* LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. |
||||
*/ |
||||
|
||||
/*
|
||||
* 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> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#include "lwip/inet_chksum.h" |
||||
#include "lwip/def.h" |
||||
#include "lwip/ip_addr.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#ifndef LWIP_CHKSUM |
||||
# define LWIP_CHKSUM lwip_standard_chksum |
||||
# 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 |
||||
# define LWIP_CHKSUM_ALGORITHM 0 |
||||
#endif |
||||
|
||||
#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ |
||||
/**
|
||||
* lwip checksum |
||||
* |
||||
* @param dataptr points to start of data to be summed at any boundary |
||||
* @param len length of data to be summed |
||||
* @return host order (!) lwip checksum (non-inverted Internet sum) |
||||
* |
||||
* @note accumulator size limits summable length to 64k |
||||
* @note host endianness is irrelevant (p3 RFC1071) |
||||
*/ |
||||
u16_t |
||||
lwip_standard_chksum(const void *dataptr, int len) |
||||
{ |
||||
u32_t acc; |
||||
u16_t src; |
||||
const u8_t *octetptr; |
||||
|
||||
acc = 0; |
||||
/* dataptr may be at odd or even addresses */ |
||||
octetptr = (const u8_t *)dataptr; |
||||
while (len > 1) { |
||||
/* declare first octet as most significant
|
||||
thus assume network order, ignoring host order */ |
||||
src = (*octetptr) << 8; |
||||
octetptr++; |
||||
/* declare second octet as least significant */ |
||||
src |= (*octetptr); |
||||
octetptr++; |
||||
acc += src; |
||||
len -= 2; |
||||
} |
||||
if (len > 0) { |
||||
/* accumulate remaining octet */ |
||||
src = (*octetptr) << 8; |
||||
acc += src; |
||||
} |
||||
/* add deferred carry bits */ |
||||
acc = (acc >> 16) + (acc & 0x0000ffffUL); |
||||
if ((acc & 0xffff0000UL) != 0) { |
||||
acc = (acc >> 16) + (acc & 0x0000ffffUL); |
||||
} |
||||
/* This maybe a little confusing: reorder sum using lwip_htons()
|
||||
instead of lwip_ntohs() since it has a little less call overhead. |
||||
The caller must invert bits for Internet sum ! */ |
||||
return lwip_htons((u16_t)acc); |
||||
} |
||||
#endif |
||||
|
||||
#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ |
||||
/*
|
||||
* Curt McDowell |
||||
* Broadcom Corp. |
||||
* csm@broadcom.com |
||||
* |
||||
* IP checksum two bytes at a time with support for |
||||
* unaligned buffer. |
||||
* Works for len up to and including 0x20000. |
||||
* by Curt McDowell, Broadcom Corp. 12/08/2005 |
||||
* |
||||
* @param dataptr points to start of data to be summed at any boundary |
||||
* @param len length of data to be summed |
||||
* @return host order (!) lwip checksum (non-inverted Internet sum) |
||||
*/ |
||||
u16_t |
||||
lwip_standard_chksum(const void *dataptr, int len) |
||||
{ |
||||
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); |
||||
|
||||
/* Get aligned to u16_t */ |
||||
if (odd && len > 0) { |
||||
((u8_t *)&t)[1] = *pb++; |
||||
len--; |
||||
} |
||||
|
||||
/* Add the bulk of the data */ |
||||
ps = (const u16_t *)(const void *)pb; |
||||
while (len > 1) { |
||||
sum += *ps++; |
||||
len -= 2; |
||||
} |
||||
|
||||
/* Consume left-over byte, if any */ |
||||
if (len > 0) { |
||||
((u8_t *)&t)[0] = *(const u8_t *)ps; |
||||
} |
||||
|
||||
/* Add end bytes */ |
||||
sum += t; |
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is probably faster than if statements... */ |
||||
sum = FOLD_U32T(sum); |
||||
sum = FOLD_U32T(sum); |
||||
|
||||
/* Swap if alignment was odd */ |
||||
if (odd) { |
||||
sum = SWAP_BYTES_IN_WORD(sum); |
||||
} |
||||
|
||||
return (u16_t)sum; |
||||
} |
||||
#endif |
||||
|
||||
#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ |
||||
/**
|
||||
* An optimized checksum routine. Basically, it uses loop-unrolling on |
||||
* the checksum loop, treating the head and tail bytes specially, whereas |
||||
* the inner loop acts on 8 bytes at a time. |
||||
* |
||||
* @arg start of buffer to be checksummed. May be an odd byte address. |
||||
* @len number of bytes in the buffer to be checksummed. |
||||
* @return host order (!) lwip checksum (non-inverted Internet sum) |
||||
* |
||||
* by Curt McDowell, Broadcom Corp. December 8th, 2005 |
||||
*/ |
||||
u16_t |
||||
lwip_standard_chksum(const void *dataptr, int len) |
||||
{ |
||||
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); |
||||
|
||||
if (odd && len > 0) { |
||||
((u8_t *)&t)[1] = *pb++; |
||||
len--; |
||||
} |
||||
|
||||
ps = (const u16_t *)(const void *)pb; |
||||
|
||||
if (((mem_ptr_t)ps & 3) && len > 1) { |
||||
sum += *ps++; |
||||
len -= 2; |
||||
} |
||||
|
||||
pl = (const u32_t *)(const void *)ps; |
||||
|
||||
while (len > 7) { |
||||
tmp = sum + *pl++; /* ping */ |
||||
if (tmp < sum) { |
||||
tmp++; /* add back carry */ |
||||
} |
||||
|
||||
sum = tmp + *pl++; /* pong */ |
||||
if (sum < tmp) { |
||||
sum++; /* add back carry */ |
||||
} |
||||
|
||||
len -= 8; |
||||
} |
||||
|
||||
/* make room in upper bits */ |
||||
sum = FOLD_U32T(sum); |
||||
|
||||
ps = (const u16_t *)pl; |
||||
|
||||
/* 16-bit aligned word remaining? */ |
||||
while (len > 1) { |
||||
sum += *ps++; |
||||
len -= 2; |
||||
} |
||||
|
||||
/* dangling tail byte remaining? */ |
||||
if (len > 0) { /* include odd byte */ |
||||
((u8_t *)&t)[0] = *(const u8_t *)ps; |
||||
} |
||||
|
||||
sum += t; /* add end bytes */ |
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is probably faster than if statements... */ |
||||
sum = FOLD_U32T(sum); |
||||
sum = FOLD_U32T(sum); |
||||
|
||||
if (odd) { |
||||
sum = SWAP_BYTES_IN_WORD(sum); |
||||
} |
||||
|
||||
return (u16_t)sum; |
||||
} |
||||
#endif |
||||
|
||||
/** 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) |
||||
{ |
||||
struct pbuf *q; |
||||
int swapped = 0; |
||||
|
||||
/* iterate through all pbuf in chain */ |
||||
for (q = p; q != NULL; q = q->next) { |
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", |
||||
(void *)q, (void *)q->next)); |
||||
acc += LWIP_CHKSUM(q->payload, q->len); |
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ |
||||
/* just executing this next line is probably faster that the if statement needed
|
||||
to check whether we really need to execute it, and does no harm */ |
||||
acc = FOLD_U32T(acc); |
||||
if (q->len % 2 != 0) { |
||||
swapped = !swapped; |
||||
acc = SWAP_BYTES_IN_WORD(acc); |
||||
} |
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ |
||||
} |
||||
|
||||
if (swapped) { |
||||
acc = SWAP_BYTES_IN_WORD(acc); |
||||
} |
||||
|
||||
acc += (u32_t)lwip_htons((u16_t)proto); |
||||
acc += (u32_t)lwip_htons(proto_len); |
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
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 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(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 = (u32_t)(acc + ((addr >> 16) & 0xffffUL)); |
||||
addr = ip4_addr_get_u32(dest); |
||||
acc = (u32_t)(acc + (addr & 0xffffUL)); |
||||
acc = (u32_t)(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 = (u32_t)(acc + (addr & 0xffffUL)); |
||||
acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL)); |
||||
addr = dest->addr[addr_part]; |
||||
acc = (u32_t)(acc + (addr & 0xffffUL)); |
||||
acc = (u32_t)(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; |
||||
int swapped = 0; |
||||
u16_t chklen; |
||||
|
||||
/* iterate through all pbuf in chain */ |
||||
for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) { |
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", |
||||
(void *)q, (void *)q->next)); |
||||
chklen = q->len; |
||||
if (chklen > chksum_len) { |
||||
chklen = chksum_len; |
||||
} |
||||
acc += LWIP_CHKSUM(q->payload, chklen); |
||||
chksum_len = (u16_t)(chksum_len - chklen); |
||||
LWIP_ASSERT("delete me", chksum_len < 0x7fff); |
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ |
||||
/* fold the upper bit down */ |
||||
acc = FOLD_U32T(acc); |
||||
if (q->len % 2 != 0) { |
||||
swapped = !swapped; |
||||
acc = SWAP_BYTES_IN_WORD(acc); |
||||
} |
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ |
||||
} |
||||
|
||||
if (swapped) { |
||||
acc = SWAP_BYTES_IN_WORD(acc); |
||||
} |
||||
|
||||
acc += (u32_t)lwip_htons((u16_t)proto); |
||||
acc += (u32_t)lwip_htons(proto_len); |
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
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 = (u32_t)(acc + ((addr >> 16) & 0xffffUL)); |
||||
addr = ip4_addr_get_u32(dest); |
||||
acc = (u32_t)(acc + (addr & 0xffffUL)); |
||||
acc = (u32_t)(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 = (u32_t)(acc + (addr & 0xffffUL)); |
||||
acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL)); |
||||
addr = dest->addr[addr_part]; |
||||
acc = (u32_t)(acc + (addr & 0xffffUL)); |
||||
acc = (u32_t)(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 |
||||
* and ICMP. |
||||
* |
||||
* @param dataptr start of the buffer to calculate the checksum (no alignment needed) |
||||
* @param len length of the buffer to calculate the checksum |
||||
* @return checksum (as u16_t) to be saved directly in the protocol header |
||||
*/ |
||||
|
||||
u16_t |
||||
inet_chksum(const void *dataptr, u16_t len) |
||||
{ |
||||
return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len); |
||||
} |
||||
|
||||
/**
|
||||
* Calculate a checksum over a chain of pbufs (without pseudo-header, much like |
||||
* inet_chksum only pbufs are used). |
||||
* |
||||
* @param p pbuf chain over that the checksum should be calculated |
||||
* @return checksum (as u16_t) to be saved directly in the protocol header |
||||
*/ |
||||
u16_t |
||||
inet_chksum_pbuf(struct pbuf *p) |
||||
{ |
||||
u32_t acc; |
||||
struct pbuf *q; |
||||
int swapped = 0; |
||||
|
||||
acc = 0; |
||||
for (q = p; q != NULL; q = q->next) { |
||||
acc += LWIP_CHKSUM(q->payload, q->len); |
||||
acc = FOLD_U32T(acc); |
||||
if (q->len % 2 != 0) { |
||||
swapped = !swapped; |
||||
acc = SWAP_BYTES_IN_WORD(acc); |
||||
} |
||||
} |
||||
|
||||
if (swapped) { |
||||
acc = SWAP_BYTES_IN_WORD(acc); |
||||
} |
||||
return (u16_t)~(acc & 0xffffUL); |
||||
} |
||||
|
||||
/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
|
||||
* like MEMCPY but generates a checksum at the same time. Since this is a |
||||
* performance-sensitive function, you might want to create your own version |
||||
* in assembly targeted at your hardware by defining it in lwipopts.h: |
||||
* #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len) |
||||
*/ |
||||
|
||||
#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */ |
||||
/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
|
||||
* For architectures with big caches, data might still be in cache when |
||||
* generating the checksum after copying. |
||||
*/ |
||||
u16_t |
||||
lwip_chksum_copy(void *dst, const void *src, u16_t len) |
||||
{ |
||||
MEMCPY(dst, src, len); |
||||
return LWIP_CHKSUM(dst, len); |
||||
} |
||||
#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */ |
||||
@ -1,390 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Modules initialization |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met: |
||||
* |
||||
* 1. Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* 3. The name of the author may not be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||||
* OF SUCH DAMAGE. |
||||
* |
||||
* This file is part of the lwIP TCP/IP stack. |
||||
* |
||||
* Author: Adam Dunkels <adam@sics.se> |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#include "lwip/init.h" |
||||
#include "lwip/stats.h" |
||||
#include "lwip/sys.h" |
||||
#include "lwip/mem.h" |
||||
#include "lwip/memp.h" |
||||
#include "lwip/pbuf.h" |
||||
#include "lwip/netif.h" |
||||
#include "lwip/sockets.h" |
||||
#include "lwip/ip.h" |
||||
#include "lwip/raw.h" |
||||
#include "lwip/udp.h" |
||||
#include "lwip/priv/tcp_priv.h" |
||||
#include "lwip/igmp.h" |
||||
#include "lwip/dns.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" |
||||
|
||||
#ifndef LWIP_SKIP_PACKING_CHECK |
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES |
||||
# include "arch/bpstruct.h" |
||||
#endif |
||||
PACK_STRUCT_BEGIN |
||||
struct packed_struct_test { |
||||
PACK_STRUCT_FLD_8(u8_t dummy1); |
||||
PACK_STRUCT_FIELD(u32_t dummy2); |
||||
} PACK_STRUCT_STRUCT; |
||||
PACK_STRUCT_END |
||||
#ifdef PACK_STRUCT_USE_INCLUDES |
||||
# include "arch/epstruct.h" |
||||
#endif |
||||
#define PACKED_STRUCT_TEST_EXPECTED_SIZE 5 |
||||
|
||||
#endif |
||||
|
||||
/* Compile-time sanity checks for configuration errors.
|
||||
* These can be done independently of LWIP_DEBUG, without penalty. |
||||
*/ |
||||
#ifndef BYTE_ORDER |
||||
#error "BYTE_ORDER is not defined, you have to define it in your cc.h" |
||||
#endif |
||||
#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) |
||||
#error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" |
||||
#endif |
||||
#if (!LWIP_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_DHCP) |
||||
#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" |
||||
#endif |
||||
#if (!LWIP_UDP && !LWIP_RAW && LWIP_MULTICAST_TX_OPTIONS) |
||||
#error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 and/or LWIP_RAW=1 in your lwipopts.h" |
||||
#endif |
||||
#if (!LWIP_UDP && LWIP_DNS) |
||||
#error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" |
||||
#endif |
||||
#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ |
||||
#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) |
||||
#error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" |
||||
#endif |
||||
#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) |
||||
#error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" |
||||
#endif |
||||
#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) |
||||
#error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" |
||||
#endif |
||||
#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) |
||||
#error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" |
||||
#endif |
||||
#if (LWIP_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_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_NUM_SYS_TIMEOUT_INTERNAL) |
||||
#error "MEMP_NUM_SYS_TIMEOUT is too low to accommodate 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_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 && (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 |
||||
#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) |
||||
#error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" |
||||
#endif |
||||
#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) |
||||
#error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" |
||||
#endif |
||||
#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))) |
||||
#error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" |
||||
#endif |
||||
#if (LWIP_TCP && LWIP_TCP_SACK_OUT && !TCP_QUEUE_OOSEQ) |
||||
#error "To use LWIP_TCP_SACK_OUT, TCP_QUEUE_OOSEQ needs to be enabled" |
||||
#endif |
||||
#if (LWIP_TCP && LWIP_TCP_SACK_OUT && (LWIP_TCP_MAX_SACK_NUM < 1)) |
||||
#error "LWIP_TCP_MAX_SACK_NUM must be greater than 0" |
||||
#endif |
||||
#if (LWIP_NETIF_API && (NO_SYS==1)) |
||||
#error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" |
||||
#endif |
||||
#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) |
||||
#error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" |
||||
#endif |
||||
#if (LWIP_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" |
||||
#endif |
||||
#if (((!LWIP_DHCP) || (!LWIP_ARP) || (!LWIP_ACD)) && LWIP_DHCP_DOES_ACD_CHECK) |
||||
#error "If you want to use DHCP ACD checking, you have to define LWIP_DHCP=1, LWIP_ARP=1 and LWIP_ACD=1 in your lwipopts.h" |
||||
#endif |
||||
#if (!LWIP_ARP && LWIP_AUTOIP) |
||||
#error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" |
||||
#endif |
||||
#if (LWIP_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 |
||||
#if (LWIP_ALTCP && LWIP_EVENT_API) |
||||
#error "The application layered tcp API does not work with LWIP_EVENT_API" |
||||
#endif |
||||
#if (MEM_CUSTOM_ALLOCATOR && !(defined(MEM_CUSTOM_FREE) && defined(MEM_CUSTOM_MALLOC) && defined(MEM_CUSTOM_CALLOC))) |
||||
#error "All of MEM_CUSTOM_FREE/MEM_CUSTOM_MALLOC/MEM_CUSTOM_CALLOC must be provided if MEM_CUSTOM_ALLOCATOR is enabled in your lwipopts.h" |
||||
#endif |
||||
#if (MEM_USE_POOLS && MEM_CUSTOM_ALLOCATOR) |
||||
#error "MEM_USE_POOLS may not be used with a custom allocator (MEM_CUSTOM_ALLOCATOR or MEM_LIBC_MALLOC) enabled in your lwipopts.h" |
||||
#endif |
||||
#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) |
||||
#error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" |
||||
#endif |
||||
#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) |
||||
#error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" |
||||
#endif |
||||
#if (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 && !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 PPP_SUPPORT && CCP_SUPPORT && !MPPE_SUPPORT |
||||
#error "CCP_SUPPORT needs MPPE_SUPPORT 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_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING |
||||
#error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" |
||||
#endif |
||||
#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE |
||||
#error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets" |
||||
#endif |
||||
#if LWIP_NETCONN && LWIP_TCP |
||||
#if NETCONN_COPY != TCP_WRITE_FLAG_COPY |
||||
#error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" |
||||
#endif |
||||
#if NETCONN_MORE != TCP_WRITE_FLAG_MORE |
||||
#error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" |
||||
#endif |
||||
#endif /* LWIP_NETCONN && LWIP_TCP */ |
||||
#if LWIP_NETCONN_FULLDUPLEX && !LWIP_NETCONN_SEM_PER_THREAD |
||||
#error "For LWIP_NETCONN_FULLDUPLEX to work, LWIP_NETCONN_SEM_PER_THREAD is required" |
||||
#endif |
||||
|
||||
|
||||
/* Compile-time checks for deprecated options.
|
||||
*/ |
||||
#ifdef MEMP_NUM_TCPIP_MSG |
||||
#error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." |
||||
#endif |
||||
#ifdef TCP_REXMIT_DEBUG |
||||
#error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." |
||||
#endif |
||||
#ifdef RAW_STATS |
||||
#error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." |
||||
#endif |
||||
#ifdef ETHARP_QUEUE_FIRST |
||||
#error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." |
||||
#endif |
||||
#ifdef ETHARP_ALWAYS_INSERT |
||||
#error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." |
||||
#endif |
||||
#if !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 |
||||
#endif |
||||
#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS |
||||
#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 |
||||
#endif |
||||
|
||||
/* MEMP sanity checks */ |
||||
#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 |
||||
#else /* MEMP_MEM_MALLOC */ |
||||
#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 /* 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 |
||||
#if LWIP_TCP |
||||
#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) |
||||
#error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." |
||||
#endif |
||||
#if TCP_SND_BUF < (2 * TCP_MSS) |
||||
#error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." |
||||
#endif |
||||
#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) |
||||
#error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." |
||||
#endif |
||||
#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_MSS >= ((16 * 1024) - 1) |
||||
#error "lwip_sanity_check: WARNING: TCP_MSS must be <= 16382 to prevent u16_t underflow in TCP_SNDLOWAT calculation!" |
||||
#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 && PBUF_POOL_SIZE && (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 && PBUF_POOL_SIZE && (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 |
||||
#error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." |
||||
#endif |
||||
#endif /* LWIP_TCP */ |
||||
#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ |
||||
|
||||
/**
|
||||
* @ingroup lwip_nosys |
||||
* Initialize all modules. |
||||
* Use this in NO_SYS mode. Use tcpip_init() otherwise. |
||||
*/ |
||||
void |
||||
lwip_init(void) |
||||
{ |
||||
#ifndef LWIP_SKIP_CONST_CHECK |
||||
int a = 0; |
||||
LWIP_UNUSED_ARG(a); |
||||
LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void *, &a) == &a); |
||||
#endif |
||||
#ifndef LWIP_SKIP_PACKING_CHECK |
||||
LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE); |
||||
#endif |
||||
|
||||
/* Modules initialization */ |
||||
stats_init(); |
||||
#if !NO_SYS |
||||
sys_init(); |
||||
#endif /* !NO_SYS */ |
||||
mem_init(); |
||||
memp_init(); |
||||
pbuf_init(); |
||||
netif_init(); |
||||
#if LWIP_IPV4 |
||||
ip_init(); |
||||
#if LWIP_ARP |
||||
etharp_init(); |
||||
#endif /* LWIP_ARP */ |
||||
#endif /* LWIP_IPV4 */ |
||||
#if LWIP_RAW |
||||
raw_init(); |
||||
#endif /* LWIP_RAW */ |
||||
#if LWIP_UDP |
||||
udp_init(); |
||||
#endif /* LWIP_UDP */ |
||||
#if LWIP_TCP |
||||
tcp_init(); |
||||
#endif /* LWIP_TCP */ |
||||
#if LWIP_IGMP |
||||
igmp_init(); |
||||
#endif /* LWIP_IGMP */ |
||||
#if LWIP_DNS |
||||
dns_init(); |
||||
#endif /* LWIP_DNS */ |
||||
#if PPP_SUPPORT |
||||
ppp_init(); |
||||
#endif |
||||
|
||||
#if LWIP_TIMERS |
||||
sys_timeouts_init(); |
||||
#endif /* LWIP_TIMERS */ |
||||
} |
||||
@ -1,167 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Common IPv4 and IPv6 code |
||||
* |
||||
* @defgroup ip IP |
||||
* @ingroup callbackstyle_api |
||||
* |
||||
* @defgroup ip4 IPv4 |
||||
* @ingroup ip |
||||
* |
||||
* @defgroup ip6 IPv6 |
||||
* @ingroup ip |
||||
* |
||||
* @defgroup ipaddr IP address handling |
||||
* @ingroup infrastructure |
||||
* |
||||
* @defgroup ip4addr IPv4 only |
||||
* @ingroup ipaddr |
||||
* |
||||
* @defgroup ip6addr IPv6 only |
||||
* @ingroup ipaddr |
||||
*/ |
||||
|
||||
/*
|
||||
* 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> |
||||
* |
||||
*/ |
||||
|
||||
#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 numeric IP address (both versions) into ASCII representation. |
||||
* returns ptr to static buffer; not reentrant! |
||||
* |
||||
* @param addr ip address in network order to convert |
||||
* @return pointer to a global static (!) buffer that holds the ASCII |
||||
* representation of addr |
||||
*/ |
||||
char *ipaddr_ntoa(const ip_addr_t *addr) |
||||
{ |
||||
if (addr == NULL) { |
||||
return NULL; |
||||
} |
||||
if (IP_IS_V6(addr)) { |
||||
return ip6addr_ntoa(ip_2_ip6(addr)); |
||||
} else { |
||||
return ip4addr_ntoa(ip_2_ip4(addr)); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup ipaddr |
||||
* Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. |
||||
* |
||||
* @param addr ip 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 *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen) |
||||
{ |
||||
if (addr == NULL) { |
||||
return NULL; |
||||
} |
||||
if (IP_IS_V6(addr)) { |
||||
return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen); |
||||
} else { |
||||
return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @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) |
||||
{ |
||||
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 */ |
||||
@ -1,557 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* |
||||
* ACD IPv4 Address Conflict Detection |
||||
* |
||||
* This is an IPv4 address conflict detection implementation for the lwIP TCP/IP |
||||
* stack. It aims to be conform to RFC5227. |
||||
* |
||||
* @defgroup acd ACD |
||||
* @ingroup ip4 |
||||
* ACD related functions |
||||
* USAGE: |
||||
* |
||||
* define @ref LWIP_ACD 1 in your lwipopts.h |
||||
* Options: |
||||
* ACD_TMR_INTERVAL msecs, |
||||
* I recommend a value of 100. The value must divide 1000 with a remainder almost 0. |
||||
* Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... |
||||
* |
||||
* For fixed IP: |
||||
* - call acd_start after selecting an IP address. The caller will be informed |
||||
* on conflict status via the callback function. |
||||
* |
||||
* With AUTOIP: |
||||
* - will be called from the autoip module. No extra's needed. |
||||
* |
||||
* With DHCP: |
||||
* - enable LWIP_DHCP_DOES_ACD_CHECK. Then it will be called from the dhcp module. |
||||
* No extra's needed. |
||||
*/ |
||||
|
||||
/*
|
||||
* |
||||
* Copyright (c) 2007 Dominik Spies <kontakt@dspies.de> |
||||
* Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com> |
||||
* 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: Jasper Verschueren <jasper.verschueren@apart-audio.com> |
||||
* Author: Dominik Spies <kontakt@dspies.de> |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
/* don't build if not configured for use in lwipopts.h */ |
||||
#if LWIP_IPV4 && LWIP_ACD |
||||
|
||||
#include <string.h> |
||||
|
||||
#include "lwip/acd.h" |
||||
#include "lwip/prot/acd.h" |
||||
|
||||
#define ACD_FOREACH(acd, acd_list) for ((acd) = acd_list; (acd) != NULL; (acd) = (acd)->next) |
||||
|
||||
#define ACD_TICKS_PER_SECOND (1000 / ACD_TMR_INTERVAL) |
||||
|
||||
/* Define good random function (LWIP_RAND) in lwipopts.h */ |
||||
#ifdef LWIP_RAND |
||||
#define LWIP_ACD_RAND(netif, acd) LWIP_RAND() |
||||
#else /* LWIP_RAND */ |
||||
#ifdef LWIP_AUTOIP_RAND |
||||
#include "lwip/autoip.h" |
||||
#define LWIP_ACD_RAND(netif, acd) LWIP_AUTOIP_RAND(netif) /* for backwards compatibility */ |
||||
#else |
||||
#define LWIP_ACD_RAND(netif, acd) ((((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ |
||||
((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
|
||||
((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
|
||||
((u32_t)((netif->hwaddr[4]) & 0xff))) + \
|
||||
(acd->sent_num)) |
||||
#endif /* LWIP_AUTOIP_RAND */ |
||||
#endif /* LWIP_RAND */ |
||||
|
||||
|
||||
#define ACD_RANDOM_PROBE_WAIT(netif, acd) (LWIP_ACD_RAND(netif, acd) % \ |
||||
(PROBE_WAIT * ACD_TICKS_PER_SECOND)) |
||||
|
||||
#define ACD_RANDOM_PROBE_INTERVAL(netif, acd) ((LWIP_ACD_RAND(netif, acd) % \ |
||||
((PROBE_MAX - PROBE_MIN) * ACD_TICKS_PER_SECOND)) + \
|
||||
(PROBE_MIN * ACD_TICKS_PER_SECOND )) |
||||
|
||||
/* Function definitions */ |
||||
static void acd_restart(struct netif *netif, struct acd *acd); |
||||
static void acd_handle_arp_conflict(struct netif *netif, struct acd *acd); |
||||
static void acd_put_in_passive_mode(struct netif *netif, struct acd *acd); |
||||
|
||||
/**
|
||||
* @ingroup acd |
||||
* Add ACD client to the client list and initialize callback function |
||||
* |
||||
* @param netif network interface on which to start the acd |
||||
* client |
||||
* @param acd acd module to be added to the list |
||||
* @param acd_conflict_callback callback to be called when conflict information |
||||
* is available |
||||
*/ |
||||
err_t |
||||
acd_add(struct netif *netif, struct acd *acd, |
||||
acd_conflict_callback_t acd_conflict_callback) |
||||
{ |
||||
struct acd *acd2; |
||||
|
||||
/* Set callback */ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
LWIP_ASSERT("acd_conflict_callback != NULL", acd_conflict_callback != NULL); |
||||
acd->acd_conflict_callback = acd_conflict_callback; |
||||
|
||||
/* Check if the acd struct is already added */ |
||||
for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) { |
||||
if (acd2 == acd) { |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_add(): acd already added to list\n")); |
||||
return ERR_OK; |
||||
} |
||||
} |
||||
|
||||
/* add acd struct to the list */ |
||||
acd->next = netif->acd_list; |
||||
netif->acd_list = acd; |
||||
|
||||
return ERR_OK; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup acd |
||||
* Remove ACD client from the client list |
||||
* |
||||
* @param netif network interface from which to remove the acd client |
||||
* @param acd acd module to be removed from the list |
||||
*/ |
||||
void |
||||
acd_remove(struct netif *netif, struct acd *acd) |
||||
{ |
||||
struct acd *acd2, *prev = NULL; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) { |
||||
if (acd2 == acd) { |
||||
if (prev) { |
||||
prev->next = acd->next; |
||||
} else { |
||||
netif->acd_list = acd->next; |
||||
} |
||||
return; |
||||
} |
||||
prev = acd2; |
||||
} |
||||
LWIP_ASSERT(("acd_remove(): acd not on list\n"), 0); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @ingroup acd |
||||
* Start ACD client |
||||
* |
||||
* @param netif network interface on which to start the acd client |
||||
* @param acd acd module to start |
||||
* @param ipaddr ip address to perform acd on |
||||
*/ |
||||
err_t |
||||
acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr) |
||||
{ |
||||
err_t result = ERR_OK; |
||||
|
||||
LWIP_UNUSED_ARG(netif); |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_start(netif=%p) %c%c%"U16_F"\n", |
||||
(void *)netif, netif->name[0], |
||||
netif->name[1], (u16_t)netif->num)); |
||||
|
||||
/* init probing state */ |
||||
acd->sent_num = 0; |
||||
acd->lastconflict = 0; |
||||
ip4_addr_copy(acd->ipaddr, ipaddr); |
||||
acd->state = ACD_STATE_PROBE_WAIT; |
||||
|
||||
acd->ttw = (u16_t)(ACD_RANDOM_PROBE_WAIT(netif, acd)); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup acd |
||||
* Stop ACD client |
||||
* |
||||
* @param acd acd module to stop |
||||
*/ |
||||
err_t |
||||
acd_stop(struct acd *acd) |
||||
{ |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_stop\n")); |
||||
|
||||
if (acd != NULL) { |
||||
acd->state = ACD_STATE_OFF; |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup acd |
||||
* Inform the ACD modules when the link goes down |
||||
* |
||||
* @param netif network interface on which to inform the ACD clients |
||||
*/ |
||||
void |
||||
acd_network_changed_link_down(struct netif *netif) |
||||
{ |
||||
struct acd *acd; |
||||
/* loop over the acd's*/ |
||||
ACD_FOREACH(acd, netif->acd_list) { |
||||
acd_stop(acd); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Has to be called in loop every ACD_TMR_INTERVAL milliseconds |
||||
*/ |
||||
void |
||||
acd_tmr(void) |
||||
{ |
||||
struct netif *netif; |
||||
struct acd *acd; |
||||
/* loop through netif's */ |
||||
NETIF_FOREACH(netif) { |
||||
ACD_FOREACH(acd, netif->acd_list) { |
||||
if (acd->lastconflict > 0) { |
||||
acd->lastconflict--; |
||||
} |
||||
|
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, |
||||
("acd_tmr() ACD-State: %"U16_F", ttw=%"U16_F"\n", |
||||
(u16_t)(acd->state), acd->ttw)); |
||||
|
||||
if (acd->ttw > 0) { |
||||
acd->ttw--; |
||||
} |
||||
|
||||
switch (acd->state) { |
||||
case ACD_STATE_PROBE_WAIT: |
||||
case ACD_STATE_PROBING: |
||||
if (acd->ttw == 0) { |
||||
acd->state = ACD_STATE_PROBING; |
||||
etharp_acd_probe(netif, &acd->ipaddr); |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, |
||||
("acd_tmr() PROBING Sent Probe\n")); |
||||
acd->sent_num++; |
||||
if (acd->sent_num >= PROBE_NUM) { |
||||
/* Switch to ANNOUNCE_WAIT: last probe is sent*/ |
||||
acd->state = ACD_STATE_ANNOUNCE_WAIT; |
||||
|
||||
acd->sent_num = 0; |
||||
|
||||
/* calculate time to wait before announcing */ |
||||
acd->ttw = (u16_t)(ANNOUNCE_WAIT * ACD_TICKS_PER_SECOND); |
||||
} else { |
||||
/* calculate time to wait to next probe */ |
||||
acd->ttw = (u16_t)(ACD_RANDOM_PROBE_INTERVAL(netif, acd)); |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case ACD_STATE_ANNOUNCE_WAIT: |
||||
case ACD_STATE_ANNOUNCING: |
||||
if (acd->ttw == 0) { |
||||
if (acd->sent_num == 0) { |
||||
acd->state = ACD_STATE_ANNOUNCING; |
||||
|
||||
/* reset conflict count to ensure fast re-probing after announcing */ |
||||
acd->num_conflicts = 0; |
||||
|
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", |
||||
ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr), |
||||
ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr))); |
||||
} |
||||
|
||||
etharp_acd_announce(netif, &acd->ipaddr); |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, |
||||
("acd_tmr() ANNOUNCING Sent Announce\n")); |
||||
acd->ttw = ANNOUNCE_INTERVAL * ACD_TICKS_PER_SECOND; |
||||
acd->sent_num++; |
||||
|
||||
if (acd->sent_num >= ANNOUNCE_NUM) { |
||||
acd->state = ACD_STATE_ONGOING; |
||||
acd->sent_num = 0; |
||||
acd->ttw = 0; |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_tmr(): changing state to ONGOING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", |
||||
ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr), |
||||
ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr))); |
||||
|
||||
/* finally, let acd user know that the address is good and can be used */ |
||||
acd->acd_conflict_callback(netif, ACD_IP_OK); |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case ACD_STATE_RATE_LIMIT: |
||||
if (acd->ttw == 0) { |
||||
/* acd should be stopped because ipaddr isn't valid any more */ |
||||
acd_stop(acd); |
||||
/* let the acd user (after rate limit interval) know that their is
|
||||
* a conflict detected. So it can restart the address acquiring |
||||
* process.*/ |
||||
acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT); |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
/* nothing to do in other states */ |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Restarts the acd module |
||||
* |
||||
* The number of conflicts is increased and the upper layer is informed. |
||||
*/ |
||||
static void |
||||
acd_restart(struct netif *netif, struct acd *acd) |
||||
{ |
||||
/* increase conflict counter. */ |
||||
acd->num_conflicts++; |
||||
|
||||
/* Decline the address */ |
||||
acd->acd_conflict_callback(netif, ACD_DECLINE); |
||||
|
||||
/* if we tried more then MAX_CONFLICTS we must limit our rate for
|
||||
* acquiring and probing addresses. compliant to RFC 5227 Section 2.1.1 */ |
||||
if (acd->num_conflicts >= MAX_CONFLICTS) { |
||||
acd->state = ACD_STATE_RATE_LIMIT; |
||||
acd->ttw = (u16_t)(RATE_LIMIT_INTERVAL * ACD_TICKS_PER_SECOND); |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, |
||||
("acd_restart(): rate limiting initiated. too many conflicts\n")); |
||||
} |
||||
else { |
||||
/* acd should be stopped because ipaddr isn't valid any more */ |
||||
acd_stop(acd); |
||||
/* let the acd user know right away that their is a conflict detected.
|
||||
* So it can restart the address acquiring process. */ |
||||
acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Handles every incoming ARP Packet, called by etharp_input(). |
||||
* |
||||
* @param netif network interface to use for acd processing |
||||
* @param hdr Incoming ARP packet |
||||
*/ |
||||
void |
||||
acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr) |
||||
{ |
||||
struct acd *acd; |
||||
ip4_addr_t sipaddr, dipaddr; |
||||
struct eth_addr netifaddr; |
||||
SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN); |
||||
|
||||
/* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support
|
||||
* compilers without structure packing (not using structure copy which |
||||
* breaks strict-aliasing rules). |
||||
*/ |
||||
IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr); |
||||
IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr); |
||||
|
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, ("acd_arp_reply()\n")); |
||||
|
||||
/* loop over the acd's*/ |
||||
ACD_FOREACH(acd, netif->acd_list) { |
||||
switch(acd->state) { |
||||
case ACD_STATE_OFF: |
||||
case ACD_STATE_RATE_LIMIT: |
||||
default: |
||||
/* do nothing */ |
||||
break; |
||||
|
||||
case ACD_STATE_PROBE_WAIT: |
||||
case ACD_STATE_PROBING: |
||||
case ACD_STATE_ANNOUNCE_WAIT: |
||||
/* RFC 5227 Section 2.1.1:
|
||||
* from beginning to after ANNOUNCE_WAIT seconds we have a conflict if |
||||
* ip.src == ipaddr (someone is already using the address) |
||||
* OR |
||||
* ip.dst == ipaddr && hw.src != own hwaddr (someone else is probing it) |
||||
*/ |
||||
if ((ip4_addr_eq(&sipaddr, &acd->ipaddr)) || |
||||
(ip4_addr_isany_val(sipaddr) && |
||||
ip4_addr_eq(&dipaddr, &acd->ipaddr) && |
||||
!eth_addr_eq(&netifaddr, &hdr->shwaddr))) { |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, |
||||
("acd_arp_reply(): Probe Conflict detected\n")); |
||||
acd_restart(netif, acd); |
||||
} |
||||
break; |
||||
|
||||
case ACD_STATE_ANNOUNCING: |
||||
case ACD_STATE_ONGOING: |
||||
case ACD_STATE_PASSIVE_ONGOING: |
||||
/* RFC 5227 Section 2.4:
|
||||
* in any state we have a conflict if |
||||
* ip.src == ipaddr && hw.src != own hwaddr (someone is using our address) |
||||
*/ |
||||
if (ip4_addr_eq(&sipaddr, &acd->ipaddr) && |
||||
!eth_addr_eq(&netifaddr, &hdr->shwaddr)) { |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, |
||||
("acd_arp_reply(): Conflicting ARP-Packet detected\n")); |
||||
acd_handle_arp_conflict(netif, acd); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Handle a IP address conflict after an ARP conflict detection |
||||
*/ |
||||
static void |
||||
acd_handle_arp_conflict(struct netif *netif, struct acd *acd) |
||||
{ |
||||
/* RFC5227, 2.4 "Ongoing Address Conflict Detection and Address Defense"
|
||||
allows three options where: |
||||
a) means retreat on the first conflict, |
||||
b) allows to keep an already configured address when having only one |
||||
conflict in DEFEND_INTERVAL seconds and |
||||
c) the host will not give up it's address and defend it indefinitely |
||||
|
||||
We use option b) when the acd module represents the netif address, since it |
||||
helps to improve the chance that one of the two conflicting hosts may be |
||||
able to retain its address. while we are flexible enough to help network |
||||
performance |
||||
|
||||
We use option a) when the acd module does not represent the netif address, |
||||
since we cannot have the acd module announcing or restarting. This |
||||
situation occurs for the LL acd module when a routable address is used on |
||||
the netif but the LL address is still open in the background. */ |
||||
|
||||
if (acd->state == ACD_STATE_PASSIVE_ONGOING) { |
||||
/* Immediately back off on a conflict. */ |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_handle_arp_conflict(): conflict when we are in passive mode -> back off\n")); |
||||
acd_stop(acd); |
||||
acd->acd_conflict_callback(netif, ACD_DECLINE); |
||||
} |
||||
else { |
||||
if (acd->lastconflict > 0) { |
||||
/* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */ |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_handle_arp_conflict(): conflict within DEFEND_INTERVAL -> retreating\n")); |
||||
|
||||
/* Active TCP sessions are aborted when removing the ip address but a bad
|
||||
* connection was inevitable anyway with conflicting hosts */ |
||||
acd_restart(netif, acd); |
||||
} else { |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_handle_arp_conflict(): we are defending, send ARP Announce\n")); |
||||
etharp_acd_announce(netif, &acd->ipaddr); |
||||
acd->lastconflict = DEFEND_INTERVAL * ACD_TICKS_PER_SECOND; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Put the acd module in passive ongoing conflict detection. |
||||
*/ |
||||
static void |
||||
acd_put_in_passive_mode(struct netif *netif, struct acd *acd) |
||||
{ |
||||
switch(acd->state) { |
||||
case ACD_STATE_OFF: |
||||
case ACD_STATE_PASSIVE_ONGOING: |
||||
default: |
||||
/* do nothing */ |
||||
break; |
||||
|
||||
case ACD_STATE_PROBE_WAIT: |
||||
case ACD_STATE_PROBING: |
||||
case ACD_STATE_ANNOUNCE_WAIT: |
||||
case ACD_STATE_RATE_LIMIT: |
||||
acd_stop(acd); |
||||
acd->acd_conflict_callback(netif, ACD_DECLINE); |
||||
break; |
||||
|
||||
case ACD_STATE_ANNOUNCING: |
||||
case ACD_STATE_ONGOING: |
||||
acd->state = ACD_STATE_PASSIVE_ONGOING; |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_put_in_passive_mode()\n")); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup acd |
||||
* Inform the ACD modules of address changes |
||||
* |
||||
* @param netif network interface on which the address is changing |
||||
* @param old_addr old ip address |
||||
* @param new_addr new ip address |
||||
*/ |
||||
void |
||||
acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr, |
||||
const ip_addr_t *new_addr) |
||||
{ |
||||
struct acd *acd; |
||||
|
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_netif_ip_addr_changed(): Address changed\n")); |
||||
|
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_netif_ip_addr_changed(): old address = %s\n", ipaddr_ntoa(old_addr))); |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_netif_ip_addr_changed(): new address = %s\n", ipaddr_ntoa(new_addr))); |
||||
|
||||
/* If we change from ANY to an IP or from an IP to ANY we do nothing */ |
||||
if (ip_addr_isany(old_addr) || ip_addr_isany(new_addr)) { |
||||
return; |
||||
} |
||||
|
||||
ACD_FOREACH(acd, netif->acd_list) { |
||||
/* Find ACD module of old address */ |
||||
if(ip4_addr_eq(&acd->ipaddr, ip_2_ip4(old_addr))) { |
||||
/* Did we change from a LL address to a routable address? */ |
||||
if (ip_addr_islinklocal(old_addr) && !ip_addr_islinklocal(new_addr)) { |
||||
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("acd_netif_ip_addr_changed(): changed from LL to routable address\n")); |
||||
/* Put the module in passive conflict detection mode */ |
||||
acd_put_in_passive_mode(netif, acd); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_ACD */ |
||||
@ -1,379 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* AutoIP Automatic LinkLocal IP Configuration |
||||
* |
||||
* This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform |
||||
* with RFC 3927. It uses IPv4 address conflict detection to evaluate the chosen |
||||
* address. The ACD module aims to be conform to RFC 5227. |
||||
* RFC 5227 is extracted out of RFC 3927 so the acd module fits nicely in autoip. |
||||
* |
||||
* @defgroup autoip AUTOIP |
||||
* @ingroup ip4 |
||||
* AUTOIP related functions |
||||
* USAGE: |
||||
* |
||||
* define @ref LWIP_AUTOIP 1 in your lwipopts.h |
||||
* |
||||
* Without DHCP: |
||||
* - Call autoip_start() after netif_add(). |
||||
* |
||||
* With DHCP: |
||||
* - define @ref LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. |
||||
* - Configure your DHCP Client. |
||||
* |
||||
* @see netifapi_autoip |
||||
*/ |
||||
|
||||
/*
|
||||
* |
||||
* Copyright (c) 2007 Dominik Spies <kontakt@dspies.de> |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met: |
||||
* |
||||
* 1. Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* 3. The name of the author may not be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||||
* OF SUCH DAMAGE. |
||||
* |
||||
* Author: Dominik Spies <kontakt@dspies.de> |
||||
*/ |
||||
|
||||
#include "lwip/opt.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/ip_addr.h" |
||||
#include "lwip/netif.h" |
||||
#include "lwip/autoip.h" |
||||
#include "lwip/acd.h" |
||||
#include "lwip/etharp.h" |
||||
#include "lwip/prot/autoip.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
/**
|
||||
* Macro that generates the initial IP address to be tried by AUTOIP. |
||||
* If you want to override this, define it to something else in lwipopts.h. |
||||
*/ |
||||
#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR |
||||
#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ |
||||
lwip_htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
|
||||
((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) |
||||
#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ |
||||
|
||||
/* Function definitions */ |
||||
static void autoip_restart(struct netif *netif); |
||||
static void autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr); |
||||
static err_t autoip_bind(struct netif *netif); |
||||
static void autoip_conflict_callback(struct netif *netif, |
||||
acd_callback_enum_t state); |
||||
|
||||
/**
|
||||
* @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 autoip (uninitialised) autoip struct allocated by the application |
||||
*/ |
||||
void |
||||
autoip_set_struct(struct netif *netif, struct autoip *autoip) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
LWIP_ASSERT("netif != NULL", netif != NULL); |
||||
LWIP_ASSERT("autoip != NULL", autoip != NULL); |
||||
LWIP_ASSERT("netif already has a struct autoip set", |
||||
netif_autoip_data(netif) == NULL); |
||||
|
||||
/* clear data structure */ |
||||
memset(autoip, 0, sizeof(struct autoip)); |
||||
/* autoip->state = AUTOIP_STATE_OFF; */ |
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup autoip |
||||
* Remove a struct autoip previously set to the netif using autoip_set_struct() |
||||
* |
||||
* @param netif the netif for which to set the struct autoip |
||||
*/ |
||||
void |
||||
autoip_remove_struct(struct netif *netif) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
LWIP_ASSERT("netif != NULL", netif != NULL); |
||||
LWIP_ASSERT("netif has no struct autoip set", |
||||
netif_autoip_data(netif) != NULL); |
||||
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, NULL); |
||||
} |
||||
|
||||
/** Restart AutoIP client and check the next address (conflict detected)
|
||||
* |
||||
* @param netif The netif under AutoIP control |
||||
*/ |
||||
static void |
||||
autoip_restart(struct netif *netif) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
autoip->tried_llipaddr++; |
||||
autoip_start(netif); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 |
||||
* |
||||
* @param netif network interface on which create the IP-Address |
||||
* @param ipaddr ip address to initialize |
||||
*/ |
||||
static void |
||||
autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
|
||||
/* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
|
||||
* compliant to RFC 3927 Section 2.1 |
||||
* We have 254 * 256 possibilities */ |
||||
|
||||
u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); |
||||
addr += autoip->tried_llipaddr; |
||||
addr = AUTOIP_NET | (addr & 0xffff); |
||||
/* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ |
||||
|
||||
if (addr < AUTOIP_RANGE_START) { |
||||
addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; |
||||
} |
||||
if (addr > AUTOIP_RANGE_END) { |
||||
addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; |
||||
} |
||||
LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && |
||||
(addr <= AUTOIP_RANGE_END)); |
||||
ip4_addr_set_u32(ipaddr, lwip_htonl(addr)); |
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", |
||||
(u16_t)(autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), |
||||
ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Configure interface for use with current LL IP-Address |
||||
* |
||||
* @param netif network interface to configure with current LL IP-Address |
||||
*/ |
||||
static err_t |
||||
autoip_bind(struct netif *netif) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
ip4_addr_t sn_mask, gw_addr; |
||||
|
||||
autoip->state = AUTOIP_STATE_BOUND; |
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, |
||||
("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", |
||||
(void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num, |
||||
ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), |
||||
ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); |
||||
|
||||
IP4_ADDR(&sn_mask, 255, 255, 0, 0); |
||||
IP4_ADDR(&gw_addr, 0, 0, 0, 0); |
||||
|
||||
netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr); |
||||
/* interface is used by routing now that an address is set */ |
||||
|
||||
return ERR_OK; |
||||
} |
||||
|
||||
/**
|
||||
* Handle conflict information from ACD module |
||||
* |
||||
* @param netif network interface to handle conflict information on |
||||
* @param state acd_callback_enum_t |
||||
*/ |
||||
static void |
||||
autoip_conflict_callback(struct netif *netif, acd_callback_enum_t state) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
|
||||
switch (state) { |
||||
case ACD_IP_OK: |
||||
autoip_bind(netif); |
||||
break; |
||||
case ACD_RESTART_CLIENT: |
||||
autoip_restart(netif); |
||||
break; |
||||
case ACD_DECLINE: |
||||
/* "delete" conflicting address so a new one will be selected in
|
||||
* autoip_start() */ |
||||
ip4_addr_set_any(&autoip->llipaddr); |
||||
autoip_stop(netif); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup autoip |
||||
* Start AutoIP client |
||||
* |
||||
* @param netif network interface on which start the AutoIP client |
||||
*/ |
||||
err_t |
||||
autoip_start(struct netif *netif) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
err_t result = ERR_OK; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;); |
||||
|
||||
if (autoip == NULL) { |
||||
/* no AutoIP client attached yet? */ |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, |
||||
("autoip_start(): starting new AUTOIP client\n")); |
||||
autoip = (struct autoip *)mem_calloc(1, sizeof(struct autoip)); |
||||
if (autoip == NULL) { |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, |
||||
("autoip_start(): could not allocate autoip\n")); |
||||
return ERR_MEM; |
||||
} |
||||
/* store this AutoIP client in the netif */ |
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip); |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip\n")); |
||||
} |
||||
|
||||
if (autoip->state == AUTOIP_STATE_OFF) { |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("autoip_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], |
||||
netif->name[1], (u16_t)netif->num)); |
||||
|
||||
/* add acd struct to list*/ |
||||
acd_add(netif, &autoip->acd, autoip_conflict_callback); |
||||
|
||||
/* In accordance to RFC3927 section 2.1:
|
||||
* Keep using the same link local address as much as possible. |
||||
* Only when there is none or when there was a conflict, select a new one. |
||||
*/ |
||||
if (!ip4_addr_islinklocal(&autoip->llipaddr)) { |
||||
autoip_create_addr(netif, &(autoip->llipaddr)); |
||||
} |
||||
autoip->state = AUTOIP_STATE_CHECKING; |
||||
acd_start(netif, &autoip->acd, autoip->llipaddr); |
||||
} else { |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, |
||||
("autoip_start(): already started on netif=%p %c%c%"U16_F"\n", |
||||
(void *)netif, netif->name[0], |
||||
netif->name[1], (u16_t)netif->num)); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Handle a possible change in the network configuration: link up |
||||
* |
||||
* If there is an AutoIP address configured and AutoIP is not in cooperation |
||||
* with DHCP, start probing for previous address. |
||||
*/ |
||||
void |
||||
autoip_network_changed_link_up(struct netif *netif) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
|
||||
if (autoip && (autoip->state != AUTOIP_STATE_OFF) && !LWIP_DHCP_AUTOIP_COOP) { |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, |
||||
("autoip_network_changed_link_up(): start acd\n")); |
||||
autoip->state = AUTOIP_STATE_CHECKING; |
||||
/* Start acd check again for the last used address */ |
||||
acd_start(netif, &autoip->acd, autoip->llipaddr); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Handle a possible change in the network configuration: link down |
||||
* |
||||
* If there is an AutoIP address configured and AutoIP is in cooperation |
||||
* with DHCP, then stop the autoip module. When the link goes up, we do not want |
||||
* the autoip module to start again. DHCP will initiate autoip when needed. |
||||
*/ |
||||
void |
||||
autoip_network_changed_link_down(struct netif *netif) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
|
||||
if (autoip && (autoip->state != AUTOIP_STATE_OFF) && LWIP_DHCP_AUTOIP_COOP) { |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, |
||||
("autoip_network_changed_link_down(): stop autoip\n")); |
||||
autoip_stop(netif); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup autoip |
||||
* Stop AutoIP client |
||||
* |
||||
* @param netif network interface on which stop the AutoIP client |
||||
*/ |
||||
err_t |
||||
autoip_stop(struct netif *netif) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
if (autoip != NULL) { |
||||
autoip->state = AUTOIP_STATE_OFF; |
||||
if (ip4_addr_islinklocal(netif_ip4_addr(netif))) { |
||||
netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4); |
||||
} |
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,("autoip_stop()\n")); |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
/** check if AutoIP supplied netif->ip_addr
|
||||
* |
||||
* @param netif the netif to check |
||||
* @return 1 if AutoIP supplied netif->ip_addr (state BOUND), |
||||
* 0 otherwise |
||||
*/ |
||||
u8_t |
||||
autoip_supplied_address(struct netif *netif) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
return (autoip != NULL) |
||||
&& (ip4_addr_eq(netif_ip4_addr(netif), &(autoip->llipaddr))) |
||||
&& (autoip->state == AUTOIP_STATE_BOUND); |
||||
} |
||||
|
||||
u8_t |
||||
autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr) |
||||
{ |
||||
struct autoip *autoip = netif_autoip_data(netif); |
||||
return (autoip != NULL) |
||||
&& (ip4_addr_eq(addr, &(autoip->llipaddr))) |
||||
&& (autoip->state == AUTOIP_STATE_BOUND); |
||||
} |
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_AUTOIP */ |
||||
@ -1,408 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* ICMP - Internet Control Message Protocol |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* 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> |
||||
* |
||||
*/ |
||||
|
||||
/* Some ICMP messages should be passed to the transport protocols. This
|
||||
is not implemented. */ |
||||
|
||||
#include "lwip/opt.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 <string.h> |
||||
|
||||
#ifdef LWIP_HOOK_FILENAME |
||||
#include LWIP_HOOK_FILENAME |
||||
#endif |
||||
|
||||
/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
|
||||
* used to modify and send a response packet (and to 1 if this is not the case, |
||||
* e.g. when link header is stripped off when receiving) */ |
||||
#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN |
||||
#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 |
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ |
||||
|
||||
/* The maximum amount of data from the original packet to return in a dest-unreachable */ |
||||
#define ICMP_DEST_UNREACH_DATASIZE 8 |
||||
|
||||
static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); |
||||
|
||||
/**
|
||||
* Processes ICMP input packets, called from ip_input(). |
||||
* |
||||
* Currently only processes icmp echo requests and sends |
||||
* out the echo response. |
||||
* |
||||
* @param p the icmp echo request packet, p->payload pointing to the icmp header |
||||
* @param inp the netif on which this packet was received |
||||
*/ |
||||
void |
||||
icmp_input(struct pbuf *p, struct netif *inp) |
||||
{ |
||||
u8_t type; |
||||
#ifdef LWIP_DEBUG |
||||
u8_t code; |
||||
#endif /* LWIP_DEBUG */ |
||||
struct icmp_echo_hdr *iecho; |
||||
const struct ip_hdr *iphdr_in; |
||||
u16_t hlen; |
||||
const ip4_addr_t *src; |
||||
|
||||
ICMP_STATS_INC(icmp.recv); |
||||
MIB2_STATS_INC(mib2.icmpinmsgs); |
||||
|
||||
iphdr_in = ip4_current_header(); |
||||
hlen = IPH_HL_BYTES(iphdr_in); |
||||
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; |
||||
} |
||||
|
||||
type = *((u8_t *)p->payload); |
||||
#ifdef LWIP_DEBUG |
||||
code = *(((u8_t *)p->payload) + 1); |
||||
/* if debug is enabled but debug statement below is somehow disabled: */ |
||||
LWIP_UNUSED_ARG(code); |
||||
#endif /* LWIP_DEBUG */ |
||||
switch (type) { |
||||
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: |
||||
MIB2_STATS_INC(mib2.icmpinechos); |
||||
src = ip4_current_dest_addr(); |
||||
/* multicast destination address? */ |
||||
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 */ |
||||
} |
||||
/* broadcast destination address? */ |
||||
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 */ |
||||
} |
||||
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); |
||||
MIB2_STATS_INC(mib2.icmpinerrors); |
||||
return; |
||||
} |
||||
} |
||||
#endif |
||||
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN |
||||
if (pbuf_add_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; |
||||
u16_t alloc_len = (u16_t)(p->tot_len + hlen); |
||||
if (alloc_len < p->tot_len) { |
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed (tot_len overflow)\n")); |
||||
goto icmperr; |
||||
} |
||||
/* allocate new packet buffer with space for link headers */ |
||||
r = pbuf_alloc(PBUF_LINK, alloc_len, PBUF_RAM); |
||||
if (r == NULL) { |
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); |
||||
goto icmperr; |
||||
} |
||||
if (r->len < hlen + sizeof(struct icmp_echo_hdr)) { |
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header\n")); |
||||
pbuf_free(r); |
||||
goto icmperr; |
||||
} |
||||
/* copy the ip header */ |
||||
MEMCPY(r->payload, iphdr_in, hlen); |
||||
/* switch r->payload back to icmp header (cannot fail) */ |
||||
if (pbuf_remove_header(r, hlen)) { |
||||
LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed", 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\n")); |
||||
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 (cannot fail) */ |
||||
if (pbuf_remove_header(p, hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN)) { |
||||
LWIP_ASSERT("icmp_input: restoring original p->payload failed", 0); |
||||
goto icmperr; |
||||
} |
||||
} |
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ |
||||
/* At this point, all checks are OK. */ |
||||
/* We generate an answer by switching the dest and src ip addresses,
|
||||
* setting the icmp type to ECHO_RESPONSE and updating the checksum. */ |
||||
iecho = (struct icmp_echo_hdr *)p->payload; |
||||
if (pbuf_add_header(p, hlen)) { |
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet\n")); |
||||
} 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); |
||||
p->if_idx = NETIF_NO_INDEX; /* we're reusing this pbuf, so reset its if_idx */ |
||||
#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))) { |
||||
iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1); |
||||
} else { |
||||
iecho->chksum = (u16_t)(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 */ |
||||
|
||||
/* Set the correct TTL and recalculate the header checksum. */ |
||||
IPH_TTL_SET(iphdr, ICMP_TTL); |
||||
IPH_CHKSUM_SET(iphdr, 0); |
||||
#if CHECKSUM_GEN_IP |
||||
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 */ |
||||
MIB2_STATS_INC(mib2.icmpoutmsgs); |
||||
/* increase number of echo replies attempted to send */ |
||||
MIB2_STATS_INC(mib2.icmpoutechoreps); |
||||
|
||||
/* send an ICMP packet */ |
||||
ret = ip4_output_if(p, src, LWIP_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: %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.icmpintimeexcds); |
||||
} 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); |
||||
ICMP_STATS_INC(icmp.drop); |
||||
} |
||||
pbuf_free(p); |
||||
return; |
||||
lenerr: |
||||
pbuf_free(p); |
||||
ICMP_STATS_INC(icmp.lenerr); |
||||
MIB2_STATS_INC(mib2.icmpinerrors); |
||||
return; |
||||
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING |
||||
icmperr: |
||||
pbuf_free(p); |
||||
ICMP_STATS_INC(icmp.err); |
||||
MIB2_STATS_INC(mib2.icmpinerrors); |
||||
return; |
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ |
||||
} |
||||
|
||||
/**
|
||||
* Send an icmp 'destination unreachable' packet, called from ip_input() if |
||||
* the transport layer protocol is unknown and from udp_input() if the local |
||||
* port is not bound. |
||||
* |
||||
* @param p the input packet for which the 'unreachable' should be sent, |
||||
* p->payload pointing to the IP header |
||||
* @param t type of the 'unreachable' packet |
||||
*/ |
||||
void |
||||
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) |
||||
{ |
||||
MIB2_STATS_INC(mib2.icmpoutdestunreachs); |
||||
icmp_send_response(p, ICMP_DUR, t); |
||||
} |
||||
|
||||
#if IP_FORWARD || IP_REASSEMBLY |
||||
/**
|
||||
* Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. |
||||
* |
||||
* @param p the input packet for which the 'time exceeded' should be sent, |
||||
* p->payload pointing to the IP header |
||||
* @param t type of the 'time exceeded' packet |
||||
*/ |
||||
void |
||||
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) |
||||
{ |
||||
MIB2_STATS_INC(mib2.icmpouttimeexcds); |
||||
icmp_send_response(p, ICMP_TE, t); |
||||
} |
||||
|
||||
#endif /* IP_FORWARD || IP_REASSEMBLY */ |
||||
|
||||
/**
|
||||
* Send an icmp packet in response to an incoming packet. |
||||
* |
||||
* @param p the input packet for which the 'unreachable' should be sent, |
||||
* p->payload pointing to the IP header |
||||
* @param type Type of the ICMP header |
||||
* @param code Code of the ICMP header |
||||
*/ |
||||
static void |
||||
icmp_send_response(struct pbuf *p, u8_t type, u8_t code) |
||||
{ |
||||
struct pbuf *q; |
||||
struct ip_hdr *iphdr; |
||||
struct icmp_hdr *icmphdr; |
||||
ip4_addr_t iphdr_src; |
||||
struct netif *netif; |
||||
u16_t response_pkt_len; |
||||
|
||||
/* increase number of messages attempted to send */ |
||||
MIB2_STATS_INC(mib2.icmpoutmsgs); |
||||
|
||||
/* Keep IP header + up to 8 bytes */ |
||||
response_pkt_len = IP_HLEN + ICMP_DEST_UNREACH_DATASIZE; |
||||
if (p->tot_len < response_pkt_len) { |
||||
response_pkt_len = p->tot_len; |
||||
} |
||||
|
||||
/* ICMP header + part of original packet */ |
||||
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_hdr) + response_pkt_len, PBUF_RAM); |
||||
if (q == NULL) { |
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_send_response: failed to allocate pbuf for ICMP packet.\n")); |
||||
MIB2_STATS_INC(mib2.icmpouterrors); |
||||
return; |
||||
} |
||||
LWIP_ASSERT("check that first pbuf can hold icmp message", |
||||
(q->len >= (sizeof(struct icmp_hdr) + response_pkt_len))); |
||||
|
||||
iphdr = (struct ip_hdr *)p->payload; |
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_send_response: Sending ICMP type %02X for packet from ", type)); |
||||
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src); |
||||
LWIP_DEBUGF(ICMP_DEBUG, (" to ")); |
||||
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest); |
||||
LWIP_DEBUGF(ICMP_DEBUG, ("\n")); |
||||
|
||||
icmphdr = (struct icmp_hdr *)q->payload; |
||||
icmphdr->type = type; |
||||
icmphdr->code = code; |
||||
icmphdr->data = 0; |
||||
|
||||
/* copy fields from original packet */ |
||||
pbuf_copy_partial_pbuf(q, p, response_pkt_len, sizeof(struct icmp_hdr)); |
||||
|
||||
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_dst, &iphdr_src); |
||||
} |
||||
#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); |
||||
ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif); |
||||
} |
||||
pbuf_free(q); |
||||
} |
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_ICMP */ |
||||
@ -1,801 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* IGMP - Internet Group Management Protocol |
||||
* |
||||
* @defgroup igmp IGMP |
||||
* @ingroup ip4 |
||||
* To be called from TCPIP thread |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2002 CITEL Technologies Ltd. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions |
||||
* are met: |
||||
* 1. Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE |
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||
* SUCH DAMAGE. |
||||
* |
||||
* This file is a contribution to the lwIP TCP/IP stack. |
||||
* The Swedish Institute of Computer Science and Adam Dunkels |
||||
* are specifically granted permission to redistribute this |
||||
* source code. |
||||
*/ |
||||
|
||||
/*-------------------------------------------------------------
|
||||
Note 1) |
||||
Although the rfc requires V1 AND V2 capability |
||||
we will only support v2 since now V1 is very old (August 1989) |
||||
V1 can be added if required |
||||
|
||||
a debug print and statistic have been implemented to |
||||
show this up. |
||||
------------------------------------------------------------- |
||||
------------------------------------------------------------- |
||||
Note 2) |
||||
A query for a specific group address (as opposed to ALLHOSTS) |
||||
has now been implemented as I am unsure if it is required |
||||
|
||||
a debug print and statistic have been implemented to |
||||
show this up. |
||||
------------------------------------------------------------- |
||||
------------------------------------------------------------- |
||||
Note 3) |
||||
The router alert rfc 2113 is implemented in outgoing packets |
||||
but not checked rigorously incoming |
||||
------------------------------------------------------------- |
||||
Steve Reynolds |
||||
------------------------------------------------------------*/ |
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* RFC 988 - Host extensions for IP multicasting - V0 |
||||
* RFC 1054 - Host extensions for IP multicasting - |
||||
* RFC 1112 - Host extensions for IP multicasting - V1 |
||||
* RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) |
||||
* RFC 3376 - Internet Group Management Protocol, Version 3 - V3 |
||||
* RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ |
||||
* RFC 2113 - IP Router Alert Option - |
||||
*----------------------------------------------------------------------------*/ |
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Includes |
||||
*----------------------------------------------------------------------------*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/igmp.h" |
||||
#include "lwip/debug.h" |
||||
#include "lwip/def.h" |
||||
#include "lwip/mem.h" |
||||
#include "lwip/ip.h" |
||||
#include "lwip/inet_chksum.h" |
||||
#include "lwip/netif.h" |
||||
#include "lwip/stats.h" |
||||
#include "lwip/prot/igmp.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr); |
||||
static err_t igmp_remove_group(struct netif *netif, struct igmp_group *group); |
||||
static void igmp_timeout(struct netif *netif, 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, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif); |
||||
static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type); |
||||
|
||||
static ip4_addr_t allsystems; |
||||
static ip4_addr_t allrouters; |
||||
|
||||
/**
|
||||
* Initialize the IGMP module |
||||
*/ |
||||
void |
||||
igmp_init(void) |
||||
{ |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); |
||||
|
||||
IP4_ADDR(&allsystems, 224, 0, 0, 1); |
||||
IP4_ADDR(&allrouters, 224, 0, 0, 2); |
||||
} |
||||
|
||||
/**
|
||||
* Start IGMP processing on interface |
||||
* |
||||
* @param netif network interface on which start IGMP processing |
||||
*/ |
||||
err_t |
||||
igmp_start(struct netif *netif) |
||||
{ |
||||
struct igmp_group *group; |
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void *)netif)); |
||||
|
||||
group = igmp_lookup_group(netif, &allsystems); |
||||
|
||||
if (group != NULL) { |
||||
group->group_state = IGMP_GROUP_IDLE_MEMBER; |
||||
group->use++; |
||||
|
||||
/* Allow the igmp messages at the MAC level */ |
||||
if (netif->igmp_mac_filter != NULL) { |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); |
||||
ip4_addr_debug_print_val(IGMP_DEBUG, allsystems); |
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); |
||||
netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER); |
||||
} |
||||
|
||||
return ERR_OK; |
||||
} |
||||
|
||||
return ERR_MEM; |
||||
} |
||||
|
||||
/**
|
||||
* Stop IGMP processing on interface |
||||
* |
||||
* @param netif network interface on which stop IGMP processing |
||||
*/ |
||||
err_t |
||||
igmp_stop(struct netif *netif) |
||||
{ |
||||
struct igmp_group *group = netif_igmp_data(netif); |
||||
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL); |
||||
|
||||
while (group != NULL) { |
||||
struct igmp_group *next = group->next; /* avoid use-after-free below */ |
||||
|
||||
/* disable the group at the MAC level */ |
||||
if (netif->igmp_mac_filter != NULL) { |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); |
||||
ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address); |
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); |
||||
netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER); |
||||
} |
||||
|
||||
/* free group */ |
||||
memp_free(MEMP_IGMP_GROUP, group); |
||||
|
||||
/* move to "next" */ |
||||
group = next; |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
/**
|
||||
* Report IGMP memberships for this interface |
||||
* |
||||
* @param netif network interface on which report IGMP memberships |
||||
*/ |
||||
void |
||||
igmp_report_groups(struct netif *netif) |
||||
{ |
||||
struct igmp_group *group = netif_igmp_data(netif); |
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void *)netif)); |
||||
|
||||
/* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ |
||||
if (group != NULL) { |
||||
group = group->next; |
||||
} |
||||
|
||||
while (group != NULL) { |
||||
igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); |
||||
group = group->next; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Search for a group in the netif's igmp group list |
||||
* |
||||
* @param ifp the network interface for which to look |
||||
* @param addr the group ip address to search for |
||||
* @return a struct igmp_group* if the group has been found, |
||||
* NULL if the group wasn't found. |
||||
*/ |
||||
struct igmp_group * |
||||
igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr) |
||||
{ |
||||
struct igmp_group *group = netif_igmp_data(ifp); |
||||
|
||||
while (group != NULL) { |
||||
if (ip4_addr_eq(&(group->group_address), addr)) { |
||||
return group; |
||||
} |
||||
group = group->next; |
||||
} |
||||
|
||||
/* to be clearer, we return NULL here instead of
|
||||
* 'group' (which is also NULL at this point). |
||||
*/ |
||||
return NULL; |
||||
} |
||||
|
||||
/**
|
||||
* Search for a specific igmp group and create a new one if not found- |
||||
* |
||||
* @param ifp the network interface for which to look |
||||
* @param addr the group ip address to search |
||||
* @return a struct igmp_group*, |
||||
* NULL on memory error. |
||||
*/ |
||||
static struct igmp_group * |
||||
igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr) |
||||
{ |
||||
struct igmp_group *group; |
||||
struct igmp_group *list_head = netif_igmp_data(ifp); |
||||
|
||||
/* Search if the group already exists */ |
||||
group = igmp_lookfor_group(ifp, addr); |
||||
if (group != NULL) { |
||||
/* Group already exists. */ |
||||
return group; |
||||
} |
||||
|
||||
/* Group doesn't exist yet, create a new one */ |
||||
group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); |
||||
if (group != NULL) { |
||||
ip4_addr_set(&(group->group_address), addr); |
||||
group->timer = 0; /* Not running */ |
||||
group->group_state = IGMP_GROUP_NON_MEMBER; |
||||
group->last_reporter_flag = 0; |
||||
group->use = 0; |
||||
|
||||
/* Ensure allsystems group is always first in list */ |
||||
if (list_head == NULL) { |
||||
/* this is the first entry in linked list */ |
||||
LWIP_ASSERT("igmp_lookup_group: first group must be allsystems", |
||||
(ip4_addr_eq(addr, &allsystems) != 0)); |
||||
group->next = NULL; |
||||
netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group); |
||||
} else { |
||||
/* append _after_ first entry */ |
||||
LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems", |
||||
(ip4_addr_eq(addr, &allsystems) == 0)); |
||||
group->next = list_head->next; |
||||
list_head->next = group; |
||||
} |
||||
} |
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group ? "" : "impossible to "))); |
||||
ip4_addr_debug_print(IGMP_DEBUG, addr); |
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)ifp)); |
||||
|
||||
return group; |
||||
} |
||||
|
||||
/**
|
||||
* Remove a group from netif's igmp group list, but don't free it yet |
||||
* |
||||
* @param group the group to remove from the netif's igmp group list |
||||
* @return ERR_OK if group was removed from the list, an err_t otherwise |
||||
*/ |
||||
static err_t |
||||
igmp_remove_group(struct netif *netif, struct igmp_group *group) |
||||
{ |
||||
err_t err = ERR_OK; |
||||
struct igmp_group *tmp_group; |
||||
|
||||
/* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ |
||||
for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) { |
||||
if (tmp_group->next == group) { |
||||
tmp_group->next = group->next; |
||||
break; |
||||
} |
||||
} |
||||
/* Group not found in netif's igmp group list */ |
||||
if (tmp_group == NULL) { |
||||
err = ERR_ARG; |
||||
} |
||||
|
||||
return err; |
||||
} |
||||
|
||||
/**
|
||||
* Called from ip_input() if a new IGMP packet is received. |
||||
* |
||||
* @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, const ip4_addr_t *dest) |
||||
{ |
||||
struct igmp_msg *igmp; |
||||
struct igmp_group *group; |
||||
struct igmp_group *groupref; |
||||
|
||||
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 */ |
||||
if (p->len < IGMP_MINLEN) { |
||||
pbuf_free(p); |
||||
IGMP_STATS_INC(igmp.lenerr); |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); |
||||
return; |
||||
} |
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); |
||||
ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->src); |
||||
LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); |
||||
ip4_addr_debug_print_val(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; |
||||
if (inet_chksum(igmp, p->len)) { |
||||
pbuf_free(p); |
||||
IGMP_STATS_INC(igmp.chkerr); |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); |
||||
return; |
||||
} |
||||
|
||||
/* Packet is ok so find an existing group */ |
||||
group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ |
||||
|
||||
/* If group can be found or create... */ |
||||
if (!group) { |
||||
pbuf_free(p); |
||||
IGMP_STATS_INC(igmp.drop); |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); |
||||
return; |
||||
} |
||||
|
||||
/* NOW ACT ON THE INCOMING MESSAGE TYPE... */ |
||||
switch (igmp->igmp_msgtype) { |
||||
case IGMP_MEMB_QUERY: |
||||
/* IGMP_MEMB_QUERY to the "all systems" address ? */ |
||||
if ((ip4_addr_eq(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))); |
||||
|
||||
if (igmp->igmp_maxresp == 0) { |
||||
IGMP_STATS_INC(igmp.rx_v1); |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); |
||||
igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; |
||||
} else { |
||||
IGMP_STATS_INC(igmp.rx_general); |
||||
} |
||||
|
||||
groupref = netif_igmp_data(inp); |
||||
|
||||
/* Do not send messages on the all systems group address! */ |
||||
/* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ |
||||
if (groupref != NULL) { |
||||
groupref = groupref->next; |
||||
} |
||||
|
||||
while (groupref) { |
||||
igmp_delaying_member(groupref, igmp->igmp_maxresp); |
||||
groupref = groupref->next; |
||||
} |
||||
} else { |
||||
/* IGMP_MEMB_QUERY to a specific group ? */ |
||||
if (!ip4_addr_isany(&igmp->igmp_group_address)) { |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); |
||||
ip4_addr_debug_print_val(IGMP_DEBUG, igmp->igmp_group_address); |
||||
if (ip4_addr_eq(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 */ |
||||
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))); |
||||
} |
||||
|
||||
if (group != NULL) { |
||||
IGMP_STATS_INC(igmp.rx_group); |
||||
igmp_delaying_member(group, igmp->igmp_maxresp); |
||||
} else { |
||||
IGMP_STATS_INC(igmp.drop); |
||||
} |
||||
} else { |
||||
IGMP_STATS_INC(igmp.proterr); |
||||
} |
||||
} |
||||
break; |
||||
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) { |
||||
/* This is on a specific group we have already looked up */ |
||||
group->timer = 0; /* stopped */ |
||||
group->group_state = IGMP_GROUP_IDLE_MEMBER; |
||||
group->last_reporter_flag = 0; |
||||
} |
||||
break; |
||||
default: |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", |
||||
igmp->igmp_msgtype, group->group_state, (void *)&group, (void *)inp)); |
||||
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 |
||||
* @param groupaddr the ip address of the group which to join |
||||
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise |
||||
*/ |
||||
err_t |
||||
igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) |
||||
{ |
||||
err_t err = ERR_VAL; /* no matching interface */ |
||||
struct netif *netif; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* make sure it is multicast address */ |
||||
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_eq(groupaddr, &allsystems)), return ERR_VAL;); |
||||
|
||||
/* loop through netif's */ |
||||
NETIF_FOREACH(netif) { |
||||
/* Should we join this interface ? */ |
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_eq(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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
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; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* 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_eq(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_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n")); |
||||
} else { |
||||
/* OK - it was new group */ |
||||
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_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, NETIF_ADD_MAC_FILTER); |
||||
} |
||||
|
||||
IGMP_STATS_INC(igmp.tx_join); |
||||
igmp_send(netif, group, IGMP_V2_MEMB_REPORT); |
||||
|
||||
igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); |
||||
|
||||
/* Need to work out where this timer comes from */ |
||||
group->group_state = IGMP_GROUP_DELAYING_MEMBER; |
||||
} |
||||
/* Increment group use */ |
||||
group->use++; |
||||
/* Join on this interface */ |
||||
return ERR_OK; |
||||
} else { |
||||
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; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* 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_eq(groupaddr, &allsystems)), return ERR_VAL;); |
||||
|
||||
/* loop through netif's */ |
||||
NETIF_FOREACH(netif) { |
||||
/* Should we leave this interface ? */ |
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_eq(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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return err; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup igmp |
||||
* Leave a group on one network interface. |
||||
* |
||||
* @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, an err_t otherwise |
||||
*/ |
||||
err_t |
||||
igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) |
||||
{ |
||||
struct igmp_group *group; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* make sure it is multicast address */ |
||||
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_eq(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;); |
||||
|
||||
/* 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_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) { |
||||
/* Remove the group from the list */ |
||||
igmp_remove_group(netif, group); |
||||
|
||||
/* If we are the last reporter for this group */ |
||||
if (group->last_reporter_flag) { |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n")); |
||||
IGMP_STATS_INC(igmp.tx_leave); |
||||
igmp_send(netif, group, IGMP_LEAVE_GROUP); |
||||
} |
||||
|
||||
/* Disable the group at the MAC level */ |
||||
if (netif->igmp_mac_filter != NULL) { |
||||
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, NETIF_DEL_MAC_FILTER); |
||||
} |
||||
|
||||
/* Free group struct */ |
||||
memp_free(MEMP_IGMP_GROUP, group); |
||||
} else { |
||||
/* Decrement group use */ |
||||
group->use--; |
||||
} |
||||
return ERR_OK; |
||||
} else { |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n")); |
||||
return ERR_VAL; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* The igmp timer function (both for NO_SYS=1 and =0) |
||||
* Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). |
||||
*/ |
||||
void |
||||
igmp_tmr(void) |
||||
{ |
||||
struct netif *netif; |
||||
|
||||
NETIF_FOREACH(netif) { |
||||
struct igmp_group *group = netif_igmp_data(netif); |
||||
|
||||
while (group != NULL) { |
||||
if (group->timer > 0) { |
||||
group->timer--; |
||||
if (group->timer == 0) { |
||||
igmp_timeout(netif, group); |
||||
} |
||||
} |
||||
group = group->next; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Called if a timeout for one group is reached. |
||||
* Sends a report for this group. |
||||
* |
||||
* @param group an igmp_group for which a timeout is reached |
||||
*/ |
||||
static void |
||||
igmp_timeout(struct netif *netif, struct igmp_group *group) |
||||
{ |
||||
/* 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_eq(&(group->group_address), &allsystems)))) { |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); |
||||
ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address); |
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)netif)); |
||||
|
||||
group->group_state = IGMP_GROUP_IDLE_MEMBER; |
||||
|
||||
IGMP_STATS_INC(igmp.tx_report); |
||||
igmp_send(netif, group, IGMP_V2_MEMB_REPORT); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Start a timer for an igmp group |
||||
* |
||||
* @param group the igmp_group for which to start a timer |
||||
* @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with |
||||
* every call to igmp_tmr()) |
||||
*/ |
||||
static void |
||||
igmp_start_timer(struct igmp_group *group, u8_t max_time) |
||||
{ |
||||
#ifdef LWIP_RAND |
||||
group->timer = (u16_t)(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; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Delaying membership report for a group if necessary |
||||
* |
||||
* @param group the igmp_group for which "delaying" membership report |
||||
* @param maxresp query delay |
||||
*/ |
||||
static void |
||||
igmp_delaying_member(struct igmp_group *group, u8_t maxresp) |
||||
{ |
||||
if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || |
||||
((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && |
||||
((group->timer == 0) || (maxresp < group->timer)))) { |
||||
igmp_start_timer(group, maxresp); |
||||
group->group_state = IGMP_GROUP_DELAYING_MEMBER; |
||||
} |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Sends an IP packet on a network interface. This function constructs the IP header |
||||
* and calculates the IP header checksum. If the source IP address is NULL, |
||||
* the IP address of the outgoing network interface is filled in as source address. |
||||
* |
||||
* @param p the packet to send (p->payload points to the data, e.g. next |
||||
protocol header; if dest == LWIP_IP_HDRINCL, p already includes an |
||||
IP header and p->payload points to that IP header) |
||||
* @param src the source IP address to send from (if src == IP4_ADDR_ANY, the |
||||
* IP address of the netif used to send is used as source address) |
||||
* @param dest the destination IP address to send the packet to |
||||
* @param netif the netif on which to send this packet |
||||
* @return ERR_OK if the packet was sent OK |
||||
* ERR_BUF if p doesn't have enough space for IP/LINK headers |
||||
* returns errors returned by netif->output |
||||
*/ |
||||
static err_t |
||||
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 ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); |
||||
} |
||||
|
||||
/**
|
||||
* Send an igmp packet to a specific group. |
||||
* |
||||
* @param group the group to which to send the packet |
||||
* @param type the type of igmp packet to send |
||||
*/ |
||||
static void |
||||
igmp_send(struct netif *netif, struct igmp_group *group, u8_t type) |
||||
{ |
||||
struct pbuf *p = NULL; |
||||
struct igmp_msg *igmp = NULL; |
||||
ip4_addr_t src = *IP4_ADDR_ANY4; |
||||
ip4_addr_t *dest = NULL; |
||||
|
||||
/* IP header + "router alert" option + IGMP header */ |
||||
p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); |
||||
|
||||
if (p) { |
||||
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))); |
||||
ip4_addr_copy(src, *netif_ip4_addr(netif)); |
||||
|
||||
if (type == IGMP_V2_MEMB_REPORT) { |
||||
dest = &(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; |
||||
ip4_addr_copy(igmp->igmp_group_address, group->group_address); |
||||
} |
||||
} |
||||
|
||||
if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { |
||||
igmp->igmp_msgtype = type; |
||||
igmp->igmp_maxresp = 0; |
||||
igmp->igmp_checksum = 0; |
||||
igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); |
||||
|
||||
igmp_ip_output_if(p, &src, dest, netif); |
||||
} |
||||
|
||||
pbuf_free(p); |
||||
} else { |
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); |
||||
IGMP_STATS_INC(igmp.memerr); |
||||
} |
||||
} |
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_IGMP */ |
||||
@ -1,323 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* This is the IPv4 address tools implementation. |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met: |
||||
* |
||||
* 1. Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* 3. 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> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_IPV4 |
||||
|
||||
#include "lwip/ip_addr.h" |
||||
#include "lwip/netif.h" |
||||
|
||||
/* used by IP4_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ |
||||
const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY); |
||||
const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST); |
||||
|
||||
/**
|
||||
* Determine if an address is a broadcast address on a network interface |
||||
* |
||||
* @param addr address to be checked |
||||
* @param netif the network interface against which the address is checked |
||||
* @return returns non-zero if the address is a broadcast address |
||||
*/ |
||||
u8_t |
||||
ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif) |
||||
{ |
||||
ip4_addr_t ipaddr; |
||||
ip4_addr_set_u32(&ipaddr, addr); |
||||
|
||||
/* all ones (broadcast) or all zeroes (old skool broadcast) */ |
||||
if ((~addr == IPADDR_ANY) || |
||||
(addr == IPADDR_ANY)) { |
||||
return 1; |
||||
/* no broadcast support on this network interface? */ |
||||
} else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) { |
||||
/* the given address cannot be a broadcast address
|
||||
* nor can we check against any broadcast addresses */ |
||||
return 0; |
||||
/* address matches network interface address exactly? => no broadcast */ |
||||
} else if (addr == ip4_addr_get_u32(netif_ip4_addr(netif))) { |
||||
return 0; |
||||
/* on the same (sub) network... */ |
||||
} else if (ip4_addr_net_eq(&ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) |
||||
/* ...and host identifier bits are all ones? =>... */ |
||||
&& ((addr & ~ip4_addr_get_u32(netif_ip4_netmask(netif))) == |
||||
(IPADDR_BROADCAST & ~ip4_addr_get_u32(netif_ip4_netmask(netif))))) { |
||||
/* => network broadcast address */ |
||||
return 1; |
||||
} else { |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
/** Checks if a netmask is valid (starting with ones, then only zeros)
|
||||
* |
||||
* @param netmask the IPv4 netmask to check (in network byte order!) |
||||
* @return 1 if the netmask is valid, 0 if it is not |
||||
*/ |
||||
u8_t |
||||
ip4_addr_netmask_valid(u32_t netmask) |
||||
{ |
||||
u32_t mask; |
||||
u32_t nm_hostorder = lwip_htonl(netmask); |
||||
|
||||
/* first, check for the first zero */ |
||||
for (mask = 1UL << 31 ; mask != 0; mask >>= 1) { |
||||
if ((nm_hostorder & mask) == 0) { |
||||
break; |
||||
} |
||||
} |
||||
/* then check that there is no one */ |
||||
for (; mask != 0; mask >>= 1) { |
||||
if ((nm_hostorder & mask) != 0) { |
||||
/* there is a one after the first zero -> invalid */ |
||||
return 0; |
||||
} |
||||
} |
||||
/* no one after the first zero -> valid */ |
||||
return 1; |
||||
} |
||||
|
||||
/**
|
||||
* Ascii internet address interpretation routine. |
||||
* The value returned is in network order. |
||||
* |
||||
* @param cp IP address in ascii representation (e.g. "127.0.0.1") |
||||
* @return ip address in network order |
||||
*/ |
||||
u32_t |
||||
ipaddr_addr(const char *cp) |
||||
{ |
||||
ip4_addr_t val; |
||||
|
||||
if (ip4addr_aton(cp, &val)) { |
||||
return ip4_addr_get_u32(&val); |
||||
} |
||||
return (IPADDR_NONE); |
||||
} |
||||
|
||||
/**
|
||||
* Check whether "cp" is a valid ascii representation |
||||
* of an Internet address and convert to a binary address. |
||||
* Returns 1 if the address is valid, 0 if not. |
||||
* This replaces inet_addr, the return value from which |
||||
* cannot distinguish between failure and a local broadcast address. |
||||
* |
||||
* @param cp IP address in ascii representation (e.g. "127.0.0.1") |
||||
* @param addr pointer to which to save the ip address in network order |
||||
* @return 1 if cp could be converted to addr, 0 on failure |
||||
*/ |
||||
int |
||||
ip4addr_aton(const char *cp, ip4_addr_t *addr) |
||||
{ |
||||
u32_t val; |
||||
u8_t base; |
||||
char c; |
||||
u32_t parts[4]; |
||||
u32_t *pp = parts; |
||||
|
||||
c = *cp; |
||||
for (;;) { |
||||
/*
|
||||
* Collect number up to ``.''. |
||||
* Values are specified as for C: |
||||
* 0x=hex, 0=octal, 1-9=decimal. |
||||
*/ |
||||
if (!lwip_isdigit(c)) { |
||||
return 0; |
||||
} |
||||
val = 0; |
||||
base = 10; |
||||
if (c == '0') { |
||||
c = *++cp; |
||||
if (c == 'x' || c == 'X') { |
||||
base = 16; |
||||
c = *++cp; |
||||
} else { |
||||
base = 8; |
||||
} |
||||
} |
||||
for (;;) { |
||||
if (lwip_isdigit(c)) { |
||||
if((base == 8) && ((u32_t)(c - '0') >= 8)) |
||||
break; |
||||
val = (val * base) + (u32_t)(c - '0'); |
||||
c = *++cp; |
||||
} else if (base == 16 && lwip_isxdigit(c)) { |
||||
val = (val << 4) | (u32_t)(c + 10 - (lwip_islower(c) ? 'a' : 'A')); |
||||
c = *++cp; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
if (c == '.') { |
||||
/*
|
||||
* Internet format: |
||||
* a.b.c.d |
||||
* a.b.c (with c treated as 16 bits) |
||||
* a.b (with b treated as 24 bits) |
||||
*/ |
||||
if (pp >= parts + 3) { |
||||
return 0; |
||||
} |
||||
*pp++ = val; |
||||
c = *++cp; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
/*
|
||||
* Check for trailing characters. |
||||
*/ |
||||
if (c != '\0' && !lwip_isspace(c)) { |
||||
return 0; |
||||
} |
||||
/*
|
||||
* Concoct the address according to |
||||
* the number of parts specified. |
||||
*/ |
||||
switch (pp - parts + 1) { |
||||
|
||||
case 0: |
||||
return 0; /* initial nondigit */ |
||||
|
||||
case 1: /* a -- 32 bits */ |
||||
break; |
||||
|
||||
case 2: /* a.b -- 8.24 bits */ |
||||
if (val > 0xffffffUL) { |
||||
return 0; |
||||
} |
||||
if (parts[0] > 0xff) { |
||||
return 0; |
||||
} |
||||
val |= parts[0] << 24; |
||||
break; |
||||
|
||||
case 3: /* a.b.c -- 8.8.16 bits */ |
||||
if (val > 0xffff) { |
||||
return 0; |
||||
} |
||||
if ((parts[0] > 0xff) || (parts[1] > 0xff)) { |
||||
return 0; |
||||
} |
||||
val |= (parts[0] << 24) | (parts[1] << 16); |
||||
break; |
||||
|
||||
case 4: /* a.b.c.d -- 8.8.8.8 bits */ |
||||
if (val > 0xff) { |
||||
return 0; |
||||
} |
||||
if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) { |
||||
return 0; |
||||
} |
||||
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); |
||||
break; |
||||
default: |
||||
LWIP_ASSERT("unhandled", 0); |
||||
break; |
||||
} |
||||
if (addr) { |
||||
ip4_addr_set_u32(addr, lwip_htonl(val)); |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
/**
|
||||
* Convert numeric IP address into decimal dotted ASCII representation. |
||||
* returns ptr to static buffer; not reentrant! |
||||
* |
||||
* @param addr ip address in network order to convert |
||||
* @return pointer to a global static (!) buffer that holds the ASCII |
||||
* representation of addr |
||||
*/ |
||||
char * |
||||
ip4addr_ntoa(const ip4_addr_t *addr) |
||||
{ |
||||
static char str[IP4ADDR_STRLEN_MAX]; |
||||
return ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX); |
||||
} |
||||
|
||||
/**
|
||||
* Same as ip4addr_ntoa, but reentrant since a user-supplied buffer is used. |
||||
* |
||||
* @param addr ip 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 * |
||||
ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen) |
||||
{ |
||||
u32_t s_addr; |
||||
char inv[3]; |
||||
char *rp; |
||||
u8_t *ap; |
||||
u8_t rem; |
||||
u8_t n; |
||||
u8_t i; |
||||
int len = 0; |
||||
|
||||
s_addr = ip4_addr_get_u32(addr); |
||||
|
||||
rp = buf; |
||||
ap = (u8_t *)&s_addr; |
||||
for (n = 0; n < 4; n++) { |
||||
i = 0; |
||||
do { |
||||
rem = *ap % (u8_t)10; |
||||
*ap /= (u8_t)10; |
||||
inv[i++] = (char)('0' + rem); |
||||
} while (*ap); |
||||
while (i--) { |
||||
if (len++ >= buflen) { |
||||
return NULL; |
||||
} |
||||
*rp++ = inv[i]; |
||||
} |
||||
if (len++ >= buflen) { |
||||
return NULL; |
||||
} |
||||
*rp++ = '.'; |
||||
ap++; |
||||
} |
||||
*--rp = 0; |
||||
return buf; |
||||
} |
||||
|
||||
#endif /* LWIP_IPV4 */ |
||||
@ -1,894 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* This is the IPv4 packet segmentation and reassembly implementation. |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met: |
||||
* |
||||
* 1. Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* 3. 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: Jani Monoses <jani@iv.ro> |
||||
* Simon Goldschmidt |
||||
* original reassembly code by Adam Dunkels <adam@sics.se> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_IPV4 |
||||
|
||||
#include "lwip/ip4_frag.h" |
||||
#include "lwip/def.h" |
||||
#include "lwip/inet_chksum.h" |
||||
#include "lwip/netif.h" |
||||
#include "lwip/stats.h" |
||||
#include "lwip/icmp.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#if IP_REASSEMBLY |
||||
/**
|
||||
* The IP reassembly code currently has the following limitations: |
||||
* - IP header options are not supported |
||||
* - fragments must not overlap (e.g. due to different routes), |
||||
* currently, overlapping or duplicate fragments are thrown away |
||||
* if IP_REASS_CHECK_OVERLAP=1 (the default)! |
||||
* |
||||
* @todo: work with IP header options |
||||
*/ |
||||
|
||||
/** 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 */ |
||||
|
||||
#define IP_REASS_FLAG_LASTFRAG 0x01 |
||||
|
||||
#define IP_REASS_VALIDATE_TELEGRAM_FINISHED 1 |
||||
#define IP_REASS_VALIDATE_PBUF_QUEUED 0 |
||||
#define IP_REASS_VALIDATE_PBUF_DROPPED -1 |
||||
|
||||
/** 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 IP header, since it replaces |
||||
* the IP header in memory in incoming fragments (after copying it) to keep |
||||
* track of the various fragments. (-> If the IP header doesn't need packing, |
||||
* this struct doesn't need packing, too.) |
||||
*/ |
||||
#ifdef PACK_STRUCT_USE_INCLUDES |
||||
# include "arch/bpstruct.h" |
||||
#endif |
||||
PACK_STRUCT_BEGIN |
||||
struct ip_reass_helper { |
||||
PACK_STRUCT_FIELD(struct pbuf *next_pbuf); |
||||
PACK_STRUCT_FIELD(u16_t start); |
||||
PACK_STRUCT_FIELD(u16_t end); |
||||
} PACK_STRUCT_STRUCT; |
||||
PACK_STRUCT_END |
||||
#ifdef PACK_STRUCT_USE_INCLUDES |
||||
# include "arch/epstruct.h" |
||||
#endif |
||||
|
||||
#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ |
||||
(ip4_addr_eq(&(iphdrA)->src, &(iphdrB)->src) && \
|
||||
ip4_addr_eq(&(iphdrA)->dest, &(iphdrB)->dest) && \
|
||||
IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 |
||||
|
||||
/* global variables */ |
||||
static struct ip_reassdata *reassdatagrams; |
||||
static u16_t ip_reass_pbufcount; |
||||
|
||||
/* function prototypes */ |
||||
static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); |
||||
static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); |
||||
|
||||
/**
|
||||
* Reassembly timer base function |
||||
* for both NO_SYS == 0 and 1 (!). |
||||
* |
||||
* Should be called every 1000 msec (defined by IP_TMR_INTERVAL). |
||||
*/ |
||||
void |
||||
ip_reass_tmr(void) |
||||
{ |
||||
struct ip_reassdata *r, *prev = NULL; |
||||
|
||||
r = reassdatagrams; |
||||
while (r != NULL) { |
||||
/* Decrement the timer. Once it reaches 0,
|
||||
* clean up the incomplete fragment assembly */ |
||||
if (r->timer > 0) { |
||||
r->timer--; |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n", (u16_t)r->timer)); |
||||
prev = r; |
||||
r = r->next; |
||||
} else { |
||||
/* reassembly timed out */ |
||||
struct ip_reassdata *tmp; |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); |
||||
tmp = r; |
||||
/* get the next pointer before freeing */ |
||||
r = r->next; |
||||
/* free the helper struct and all enqueued pbufs */ |
||||
ip_reass_free_complete_datagram(tmp, prev); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Free a datagram (struct ip_reassdata) and all its pbufs. |
||||
* Updates the total count of enqueued pbufs (ip_reass_pbufcount), |
||||
* SNMP counters and sends an ICMP time exceeded packet. |
||||
* |
||||
* @param ipr datagram to free |
||||
* @param prev the previous datagram in the linked list |
||||
* @return the number of pbufs freed |
||||
*/ |
||||
static int |
||||
ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) |
||||
{ |
||||
u16_t pbufs_freed = 0; |
||||
u16_t clen; |
||||
struct pbuf *p; |
||||
struct ip_reass_helper *iprh; |
||||
|
||||
LWIP_ASSERT("prev != ipr", prev != ipr); |
||||
if (prev != NULL) { |
||||
LWIP_ASSERT("prev->next == ipr", prev->next == ipr); |
||||
} |
||||
|
||||
MIB2_STATS_INC(mib2.ipreasmfails); |
||||
#if LWIP_ICMP |
||||
iprh = (struct ip_reass_helper *)ipr->p->payload; |
||||
if (iprh->start == 0) { |
||||
/* The first fragment was received, send ICMP time exceeded. */ |
||||
/* First, de-queue the first pbuf from r->p. */ |
||||
p = ipr->p; |
||||
ipr->p = iprh->next_pbuf; |
||||
/* Then, copy the original header into it. */ |
||||
SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); |
||||
icmp_time_exceeded(p, ICMP_TE_FRAG); |
||||
clen = pbuf_clen(p); |
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); |
||||
pbufs_freed = (u16_t)(pbufs_freed + clen); |
||||
pbuf_free(p); |
||||
} |
||||
#endif /* LWIP_ICMP */ |
||||
|
||||
/* First, free all received pbufs. The individual pbufs need to be released
|
||||
separately as they have not yet been chained */ |
||||
p = ipr->p; |
||||
while (p != NULL) { |
||||
struct pbuf *pcur; |
||||
iprh = (struct ip_reass_helper *)p->payload; |
||||
pcur = p; |
||||
/* get the next pointer before freeing */ |
||||
p = iprh->next_pbuf; |
||||
clen = pbuf_clen(pcur); |
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); |
||||
pbufs_freed = (u16_t)(pbufs_freed + clen); |
||||
pbuf_free(pcur); |
||||
} |
||||
/* Then, unchain the struct ip_reassdata from the list and free it. */ |
||||
ip_reass_dequeue_datagram(ipr, prev); |
||||
LWIP_ASSERT("ip_reass_pbufcount >= pbufs_freed", ip_reass_pbufcount >= pbufs_freed); |
||||
ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - pbufs_freed); |
||||
|
||||
return pbufs_freed; |
||||
} |
||||
|
||||
#if IP_REASS_FREE_OLDEST |
||||
/**
|
||||
* Free the oldest datagram to make room for enqueueing new fragments. |
||||
* The datagram 'fraghdr' belongs to is not freed! |
||||
* |
||||
* @param fraghdr IP header of the current fragment |
||||
* @param pbufs_needed number of pbufs needed to enqueue |
||||
* (used for freeing other datagrams if not enough space) |
||||
* @return the number of pbufs freed |
||||
*/ |
||||
static int |
||||
ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) |
||||
{ |
||||
/* @todo Can't we simply remove the last datagram in the
|
||||
* linked list behind reassdatagrams? |
||||
*/ |
||||
struct ip_reassdata *r, *oldest, *prev, *oldest_prev; |
||||
int pbufs_freed = 0, pbufs_freed_current; |
||||
int other_datagrams; |
||||
|
||||
/* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
|
||||
* but don't free the datagram that 'fraghdr' belongs to! */ |
||||
do { |
||||
oldest = NULL; |
||||
prev = NULL; |
||||
oldest_prev = NULL; |
||||
other_datagrams = 0; |
||||
r = reassdatagrams; |
||||
while (r != NULL) { |
||||
if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { |
||||
/* Not the same datagram as fraghdr */ |
||||
other_datagrams++; |
||||
if (oldest == NULL) { |
||||
oldest = r; |
||||
oldest_prev = prev; |
||||
} else if (r->timer <= oldest->timer) { |
||||
/* older than the previous oldest */ |
||||
oldest = r; |
||||
oldest_prev = prev; |
||||
} |
||||
} |
||||
if (r->next != NULL) { |
||||
prev = r; |
||||
} |
||||
r = r->next; |
||||
} |
||||
if (oldest != NULL) { |
||||
pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev); |
||||
pbufs_freed += pbufs_freed_current; |
||||
} |
||||
} while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); |
||||
return pbufs_freed; |
||||
} |
||||
#endif /* IP_REASS_FREE_OLDEST */ |
||||
|
||||
/**
|
||||
* Enqueues a new fragment into the fragment queue |
||||
* @param fraghdr points to the new fragments IP hdr |
||||
* @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) |
||||
* @return A pointer to the queue location into which the fragment was enqueued |
||||
*/ |
||||
static struct ip_reassdata * |
||||
ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) |
||||
{ |
||||
struct ip_reassdata *ipr; |
||||
#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) { |
||||
#if IP_REASS_FREE_OLDEST |
||||
if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { |
||||
ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); |
||||
} |
||||
if (ipr == NULL) |
||||
#endif /* IP_REASS_FREE_OLDEST */ |
||||
{ |
||||
IPFRAG_STATS_INC(ip_frag.memerr); |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("Failed to alloc reassdata struct\n")); |
||||
return NULL; |
||||
} |
||||
} |
||||
memset(ipr, 0, sizeof(struct ip_reassdata)); |
||||
ipr->timer = IP_REASS_MAXAGE; |
||||
|
||||
/* enqueue the new structure to the front of the list */ |
||||
ipr->next = reassdatagrams; |
||||
reassdatagrams = ipr; |
||||
/* copy the ip header for later tests and input */ |
||||
/* @todo: no ip options supported? */ |
||||
SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); |
||||
return ipr; |
||||
} |
||||
|
||||
/**
|
||||
* Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. |
||||
* @param ipr points to the queue entry to dequeue |
||||
*/ |
||||
static void |
||||
ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) |
||||
{ |
||||
/* dequeue the reass struct */ |
||||
if (reassdatagrams == ipr) { |
||||
/* it was the first in the list */ |
||||
reassdatagrams = ipr->next; |
||||
} else { |
||||
/* it wasn't the first, so it must have a valid 'prev' */ |
||||
LWIP_ASSERT("sanity check linked list", prev != NULL); |
||||
prev->next = ipr->next; |
||||
} |
||||
|
||||
/* now we can free the ip_reassdata struct */ |
||||
memp_free(MEMP_REASSDATA, ipr); |
||||
} |
||||
|
||||
/**
|
||||
* Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list |
||||
* will grow over time as new pbufs are rx. |
||||
* Also checks that the datagram passes basic continuity checks (if the last |
||||
* fragment was received at least once). |
||||
* @param ipr points to the reassembly state |
||||
* @param new_p points to the pbuf for the current fragment |
||||
* @param is_last is 1 if this pbuf has MF==0 (ipr->flags not updated yet) |
||||
* @return see IP_REASS_VALIDATE_* defines |
||||
*/ |
||||
static int |
||||
ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p, int is_last) |
||||
{ |
||||
struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev = NULL; |
||||
struct pbuf *q; |
||||
u16_t offset, len; |
||||
u8_t hlen; |
||||
struct ip_hdr *fraghdr; |
||||
int valid = 1; |
||||
|
||||
/* Extract length and fragment offset from current fragment */ |
||||
fraghdr = (struct ip_hdr *)new_p->payload; |
||||
len = lwip_ntohs(IPH_LEN(fraghdr)); |
||||
hlen = IPH_HL_BYTES(fraghdr); |
||||
if (hlen > len) { |
||||
/* invalid datagram */ |
||||
return IP_REASS_VALIDATE_PBUF_DROPPED; |
||||
} |
||||
len = (u16_t)(len - hlen); |
||||
offset = IPH_OFFSET_BYTES(fraghdr); |
||||
|
||||
/* overwrite the fragment's ip header from the pbuf with our helper struct,
|
||||
* and setup the embedded helper structure. */ |
||||
/* make sure the struct ip_reass_helper fits into the IP header */ |
||||
LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", |
||||
sizeof(struct ip_reass_helper) <= IP_HLEN); |
||||
iprh = (struct ip_reass_helper *)new_p->payload; |
||||
iprh->next_pbuf = NULL; |
||||
iprh->start = offset; |
||||
iprh->end = (u16_t)(offset + len); |
||||
if (iprh->end < offset) { |
||||
/* u16_t overflow, cannot handle this */ |
||||
return IP_REASS_VALIDATE_PBUF_DROPPED; |
||||
} |
||||
|
||||
/* Iterate through until we either get to the end of the list (append),
|
||||
* 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) { |
||||
/* the new pbuf should be inserted before this */ |
||||
iprh->next_pbuf = q; |
||||
if (iprh_prev != NULL) { |
||||
/* not the fragment with the lowest offset */ |
||||
#if IP_REASS_CHECK_OVERLAP |
||||
if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { |
||||
/* fragment overlaps with previous or following, throw away */ |
||||
return IP_REASS_VALIDATE_PBUF_DROPPED; |
||||
} |
||||
#endif /* IP_REASS_CHECK_OVERLAP */ |
||||
iprh_prev->next_pbuf = new_p; |
||||
if (iprh_prev->end != iprh->start) { |
||||
/* There is a fragment missing between the current
|
||||
* and the previous fragment */ |
||||
valid = 0; |
||||
} |
||||
} else { |
||||
#if IP_REASS_CHECK_OVERLAP |
||||
if (iprh->end > iprh_tmp->start) { |
||||
/* fragment overlaps with following, throw away */ |
||||
return IP_REASS_VALIDATE_PBUF_DROPPED; |
||||
} |
||||
#endif /* IP_REASS_CHECK_OVERLAP */ |
||||
/* fragment with the lowest offset */ |
||||
ipr->p = new_p; |
||||
} |
||||
break; |
||||
} else if (iprh->start == iprh_tmp->start) { |
||||
/* received the same datagram twice: no need to keep the datagram */ |
||||
return IP_REASS_VALIDATE_PBUF_DROPPED; |
||||
#if IP_REASS_CHECK_OVERLAP |
||||
} else if (iprh->start < iprh_tmp->end) { |
||||
/* overlap: no need to keep the new datagram */ |
||||
return IP_REASS_VALIDATE_PBUF_DROPPED; |
||||
#endif /* IP_REASS_CHECK_OVERLAP */ |
||||
} else { |
||||
/* 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
|
||||
* and the previous fragment */ |
||||
valid = 0; |
||||
} |
||||
} |
||||
} |
||||
q = iprh_tmp->next_pbuf; |
||||
iprh_prev = iprh_tmp; |
||||
} |
||||
|
||||
/* If q is NULL, then we made it to the end of the list. Determine what to do now */ |
||||
if (q == NULL) { |
||||
if (iprh_prev != NULL) { |
||||
/* this is (for now), the fragment with the highest offset:
|
||||
* chain it to the last fragment */ |
||||
#if IP_REASS_CHECK_OVERLAP |
||||
LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); |
||||
#endif /* IP_REASS_CHECK_OVERLAP */ |
||||
iprh_prev->next_pbuf = new_p; |
||||
if (iprh_prev->end != iprh->start) { |
||||
valid = 0; |
||||
} |
||||
} else { |
||||
#if IP_REASS_CHECK_OVERLAP |
||||
LWIP_ASSERT("no previous fragment, this must be the first fragment!", |
||||
ipr->p == NULL); |
||||
#endif /* IP_REASS_CHECK_OVERLAP */ |
||||
/* this is the first fragment we ever received for this ip datagram */ |
||||
ipr->p = new_p; |
||||
} |
||||
} |
||||
|
||||
/* At this point, the validation part begins: */ |
||||
/* If we already received the last fragment */ |
||||
if (is_last || ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0)) { |
||||
/* 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 ((ipr->p == NULL) || (((struct ip_reass_helper *)ipr->p->payload)->start != 0)) { |
||||
valid = 0; |
||||
} else { |
||||
/* and check that there are no holes after this datagram */ |
||||
iprh_prev = iprh; |
||||
q = iprh->next_pbuf; |
||||
while (q != NULL) { |
||||
iprh = (struct ip_reass_helper *)q->payload; |
||||
if (iprh_prev->end != iprh->start) { |
||||
valid = 0; |
||||
break; |
||||
} |
||||
iprh_prev = iprh; |
||||
q = iprh->next_pbuf; |
||||
} |
||||
/* if still valid, all fragments are received
|
||||
* (because to the MF==0 already arrived */ |
||||
if (valid) { |
||||
LWIP_ASSERT("sanity check", ipr->p != NULL); |
||||
LWIP_ASSERT("sanity check", |
||||
((struct ip_reass_helper *)ipr->p->payload) != iprh); |
||||
LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", |
||||
iprh->next_pbuf == NULL); |
||||
} |
||||
} |
||||
} |
||||
/* If valid is 0 here, there are some fragments missing in the middle
|
||||
* (since MF == 0 has already arrived). Such datagrams simply time out if |
||||
* no more fragments are received... */ |
||||
return valid ? IP_REASS_VALIDATE_TELEGRAM_FINISHED : IP_REASS_VALIDATE_PBUF_QUEUED; |
||||
} |
||||
/* If we come here, not all fragments were received, yet! */ |
||||
return IP_REASS_VALIDATE_PBUF_QUEUED; /* not yet valid! */ |
||||
} |
||||
|
||||
/**
|
||||
* Reassembles incoming IP fragments into an IP datagram. |
||||
* |
||||
* @param p points to a pbuf chain of the fragment |
||||
* @return NULL if reassembly is incomplete, ? otherwise |
||||
*/ |
||||
struct pbuf * |
||||
ip4_reass(struct pbuf *p) |
||||
{ |
||||
struct pbuf *r; |
||||
struct ip_hdr *fraghdr; |
||||
struct ip_reassdata *ipr; |
||||
struct ip_reass_helper *iprh; |
||||
u16_t offset, len, clen; |
||||
u8_t hlen; |
||||
int valid; |
||||
int is_last; |
||||
|
||||
IPFRAG_STATS_INC(ip_frag.recv); |
||||
MIB2_STATS_INC(mib2.ipreasmreqds); |
||||
|
||||
fraghdr = (struct ip_hdr *)p->payload; |
||||
|
||||
if (IPH_HL_BYTES(fraghdr) != IP_HLEN) { |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: IP options currently not supported!\n")); |
||||
IPFRAG_STATS_INC(ip_frag.err); |
||||
goto nullreturn; |
||||
} |
||||
|
||||
offset = IPH_OFFSET_BYTES(fraghdr); |
||||
len = lwip_ntohs(IPH_LEN(fraghdr)); |
||||
hlen = IPH_HL_BYTES(fraghdr); |
||||
if (hlen > len) { |
||||
/* invalid datagram */ |
||||
goto nullreturn; |
||||
} |
||||
len = (u16_t)(len - hlen); |
||||
|
||||
/* Check if we are allowed to enqueue more datagrams. */ |
||||
clen = pbuf_clen(p); |
||||
if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { |
||||
#if IP_REASS_FREE_OLDEST |
||||
if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || |
||||
((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) |
||||
#endif /* IP_REASS_FREE_OLDEST */ |
||||
{ |
||||
/* No datagram could be freed and still too many pbufs enqueued */ |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("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? */ |
||||
/* drop this pbuf */ |
||||
goto nullreturn; |
||||
} |
||||
} |
||||
|
||||
/* Look for the datagram the fragment belongs to in the current datagram queue,
|
||||
* remembering the previous in the queue for later dequeueing. */ |
||||
for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { |
||||
/* Check if the incoming fragment matches the one currently present
|
||||
in the reassembly buffer. If so, we proceed with copying the |
||||
fragment into the buffer. */ |
||||
if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n", |
||||
lwip_ntohs(IPH_ID(fraghdr)))); |
||||
IPFRAG_STATS_INC(ip_frag.cachehit); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (ipr == NULL) { |
||||
/* Enqueue a new datagram into the datagram queue */ |
||||
ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); |
||||
/* Bail if unable to enqueue */ |
||||
if (ipr == NULL) { |
||||
goto nullreturn; |
||||
} |
||||
} else { |
||||
if (((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && |
||||
((lwip_ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { |
||||
/* ipr->iphdr is not the header from the first fragment, but fraghdr is
|
||||
* -> copy fraghdr into ipr->iphdr since we want to have the header |
||||
* of the first fragment (for ICMP time exceeded and later, for copying |
||||
* all options, if supported)*/ |
||||
SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); |
||||
} |
||||
} |
||||
|
||||
/* At this point, we have either created a new entry or pointing
|
||||
* to an existing one */ |
||||
|
||||
/* check for 'no more fragments', and update queue entry*/ |
||||
is_last = (IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0; |
||||
if (is_last) { |
||||
u16_t datagram_len = (u16_t)(offset + len); |
||||
if ((datagram_len < offset) || (datagram_len > (0xFFFF - IP_HLEN))) { |
||||
/* u16_t overflow, cannot handle this */ |
||||
goto nullreturn_ipr; |
||||
} |
||||
} |
||||
/* find the right place to insert this pbuf */ |
||||
/* @todo: trim pbufs if fragments are overlapping */ |
||||
valid = ip_reass_chain_frag_into_datagram_and_validate(ipr, p, is_last); |
||||
if (valid == IP_REASS_VALIDATE_PBUF_DROPPED) { |
||||
goto nullreturn_ipr; |
||||
} |
||||
/* if we come here, the pbuf has been enqueued */ |
||||
|
||||
/* 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 |
||||
(overflow checked by testing against IP_REASS_MAX_PBUFS) */ |
||||
ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount + clen); |
||||
if (is_last) { |
||||
u16_t datagram_len = (u16_t)(offset + len); |
||||
ipr->datagram_len = datagram_len; |
||||
ipr->flags |= IP_REASS_FLAG_LASTFRAG; |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, |
||||
("ip4_reass: last fragment seen, total len %"S16_F"\n", |
||||
ipr->datagram_len)); |
||||
} |
||||
|
||||
if (valid == IP_REASS_VALIDATE_TELEGRAM_FINISHED) { |
||||
struct ip_reassdata *ipr_prev; |
||||
/* the totally last fragment (flag more fragments = 0) was received at least
|
||||
* once AND all fragments are received */ |
||||
u16_t datagram_len = (u16_t)(ipr->datagram_len + IP_HLEN); |
||||
|
||||
/* save the second pbuf before copying the header over the pointer */ |
||||
r = ((struct ip_reass_helper *)ipr->p->payload)->next_pbuf; |
||||
|
||||
/* copy the original ip header back to the first pbuf */ |
||||
fraghdr = (struct ip_hdr *)(ipr->p->payload); |
||||
SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); |
||||
IPH_LEN_SET(fraghdr, lwip_htons(datagram_len)); |
||||
IPH_OFFSET_SET(fraghdr, 0); |
||||
IPH_CHKSUM_SET(fraghdr, 0); |
||||
/* @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; |
||||
|
||||
/* chain together the pbufs contained within the reass_data list. */ |
||||
while (r != NULL) { |
||||
iprh = (struct ip_reass_helper *)r->payload; |
||||
|
||||
/* hide the ip header for every succeeding fragment */ |
||||
pbuf_remove_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. */ |
||||
clen = pbuf_clen(p); |
||||
LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen); |
||||
ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen); |
||||
|
||||
MIB2_STATS_INC(mib2.ipreasmoks); |
||||
|
||||
/* Return the pbuf chain */ |
||||
return p; |
||||
} |
||||
/* the datagram is not (yet?) reassembled completely */ |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); |
||||
return NULL; |
||||
|
||||
nullreturn_ipr: |
||||
LWIP_ASSERT("ipr != NULL", ipr != NULL); |
||||
if (ipr->p == NULL) { |
||||
/* dropped pbuf after creating a new datagram entry: remove the entry, too */ |
||||
LWIP_ASSERT("not firstalthough just enqueued", ipr == reassdatagrams); |
||||
ip_reass_dequeue_datagram(ipr, NULL); |
||||
} |
||||
|
||||
nullreturn: |
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: nullreturn\n")); |
||||
IPFRAG_STATS_INC(ip_frag.drop); |
||||
pbuf_free(p); |
||||
return NULL; |
||||
} |
||||
#endif /* IP_REASSEMBLY */ |
||||
|
||||
#if IP_FRAG |
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF |
||||
/** Allocate a new struct pbuf_custom_ref */ |
||||
static struct pbuf_custom_ref * |
||||
ip_frag_alloc_pbuf_custom_ref(void) |
||||
{ |
||||
return (struct pbuf_custom_ref *)memp_malloc(MEMP_FRAG_PBUF); |
||||
} |
||||
|
||||
/** Free a struct pbuf_custom_ref */ |
||||
static void |
||||
ip_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 |
||||
ipfrag_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); |
||||
} |
||||
ip_frag_free_pbuf_custom_ref(pcr); |
||||
} |
||||
#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ |
||||
|
||||
/**
|
||||
* Fragment an IP datagram if too large for the netif. |
||||
* |
||||
* Chop the datagram in MTU sized chunks and send them in order |
||||
* by pointing PBUF_REFs into p. |
||||
* |
||||
* @param p ip packet to send |
||||
* @param netif the netif on which to send |
||||
* @param dest destination ip address to which to send |
||||
* |
||||
* @return ERR_OK if sent successfully, err_t otherwise |
||||
*/ |
||||
err_t |
||||
ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) |
||||
{ |
||||
struct pbuf *rambuf; |
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF |
||||
struct pbuf *newpbuf; |
||||
u16_t newpbuflen = 0; |
||||
u16_t left_to_copy; |
||||
#endif |
||||
struct ip_hdr *original_iphdr; |
||||
struct ip_hdr *iphdr; |
||||
const u16_t nfb = (u16_t)((netif->mtu - IP_HLEN) / 8); |
||||
u16_t left, fragsize; |
||||
u16_t ofo; |
||||
int last; |
||||
u16_t poff = IP_HLEN; |
||||
u16_t tmp; |
||||
int mf_set; |
||||
|
||||
original_iphdr = (struct ip_hdr *)p->payload; |
||||
iphdr = original_iphdr; |
||||
if (IPH_HL_BYTES(iphdr) != IP_HLEN) { |
||||
/* ip4_frag() does not support IP options */ |
||||
return ERR_VAL; |
||||
} |
||||
LWIP_ERROR("ip4_frag(): pbuf too short", p->len >= IP_HLEN, return ERR_VAL); |
||||
|
||||
/* Save original offset */ |
||||
tmp = lwip_ntohs(IPH_OFFSET(iphdr)); |
||||
ofo = tmp & IP_OFFMASK; |
||||
/* already fragmented? if so, the last fragment we create must have MF, too */ |
||||
mf_set = tmp & IP_MF; |
||||
|
||||
left = (u16_t)(p->tot_len - IP_HLEN); |
||||
|
||||
while (left) { |
||||
/* Fill this fragment */ |
||||
fragsize = LWIP_MIN(left, (u16_t)(nfb * 8)); |
||||
|
||||
#if LWIP_NETIF_TX_SINGLE_PBUF |
||||
rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM); |
||||
if (rambuf == NULL) { |
||||
goto memerr; |
||||
} |
||||
LWIP_ASSERT("this needs a pbuf in one piece!", |
||||
(rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); |
||||
poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff); |
||||
/* make room for the IP header */ |
||||
if (pbuf_add_header(rambuf, IP_HLEN)) { |
||||
pbuf_free(rambuf); |
||||
goto memerr; |
||||
} |
||||
/* fill in the IP header */ |
||||
SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); |
||||
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. |
||||
* The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, |
||||
* but limited to the size of an mtu. |
||||
*/ |
||||
rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); |
||||
if (rambuf == NULL) { |
||||
goto memerr; |
||||
} |
||||
LWIP_ASSERT("this needs a pbuf in one piece!", |
||||
(rambuf->len >= (IP_HLEN))); |
||||
SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); |
||||
iphdr = (struct ip_hdr *)rambuf->payload; |
||||
|
||||
left_to_copy = fragsize; |
||||
while (left_to_copy) { |
||||
struct pbuf_custom_ref *pcr; |
||||
u16_t plen = (u16_t)(p->len - poff); |
||||
LWIP_ASSERT("p->len >= poff", p->len >= poff); |
||||
newpbuflen = LWIP_MIN(left_to_copy, plen); |
||||
/* Is this pbuf already empty? */ |
||||
if (!newpbuflen) { |
||||
poff = 0; |
||||
p = p->next; |
||||
continue; |
||||
} |
||||
pcr = ip_frag_alloc_pbuf_custom_ref(); |
||||
if (pcr == NULL) { |
||||
pbuf_free(rambuf); |
||||
goto memerr; |
||||
} |
||||
/* Mirror this pbuf, although we might not need all of it. */ |
||||
newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, |
||||
(u8_t *)p->payload + poff, newpbuflen); |
||||
if (newpbuf == NULL) { |
||||
ip_frag_free_pbuf_custom_ref(pcr); |
||||
pbuf_free(rambuf); |
||||
goto memerr; |
||||
} |
||||
pbuf_ref(p); |
||||
pcr->original = p; |
||||
pcr->pc.custom_free_function = ipfrag_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 = (u16_t)(left_to_copy - newpbuflen); |
||||
if (left_to_copy) { |
||||
poff = 0; |
||||
p = p->next; |
||||
} |
||||
} |
||||
poff = (u16_t)(poff + newpbuflen); |
||||
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ |
||||
|
||||
/* Correct header */ |
||||
last = (left <= netif->mtu - IP_HLEN); |
||||
|
||||
/* Set new offset and MF flag */ |
||||
tmp = (IP_OFFMASK & (ofo)); |
||||
if (!last || mf_set) { |
||||
/* the last fragment has MF set if the input frame had it */ |
||||
tmp = tmp | IP_MF; |
||||
} |
||||
IPH_OFFSET_SET(iphdr, lwip_htons(tmp)); |
||||
IPH_LEN_SET(iphdr, lwip_htons((u16_t)(fragsize + 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 */ |
||||
|
||||
/* No need for separate header pbuf - we allowed room for it in rambuf
|
||||
* when allocated. |
||||
*/ |
||||
netif->output(netif, rambuf, dest); |
||||
IPFRAG_STATS_INC(ip_frag.xmit); |
||||
|
||||
/* Unfortunately we can't reuse rambuf - the hardware may still be
|
||||
* using the buffer. Instead we free it (and the ensuing chain) and |
||||
* recreate it next time round the loop. If we're lucky the hardware |
||||
* will have already sent the packet, the free will really free, and |
||||
* there will be zero memory penalty. |
||||
*/ |
||||
|
||||
pbuf_free(rambuf); |
||||
left = (u16_t)(left - fragsize); |
||||
ofo = (u16_t)(ofo + nfb); |
||||
} |
||||
MIB2_STATS_INC(mib2.ipfragoks); |
||||
return ERR_OK; |
||||
memerr: |
||||
MIB2_STATS_INC(mib2.ipfragfails); |
||||
return ERR_MEM; |
||||
} |
||||
#endif /* IP_FRAG */ |
||||
|
||||
#endif /* LWIP_IPV4 */ |
||||
@ -1,821 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* |
||||
* @defgroup dhcp6 DHCPv6 |
||||
* @ingroup ip6 |
||||
* DHCPv6 client: IPv6 address autoconfiguration as per |
||||
* RFC 3315 (stateful DHCPv6) and |
||||
* RFC 3736 (stateless DHCPv6). |
||||
* |
||||
* For now, only stateless DHCPv6 is implemented! |
||||
* |
||||
* TODO: |
||||
* - enable/disable API to not always start when RA is received |
||||
* - stateful DHCPv6 (for now, only stateless DHCPv6 for DNS and NTP servers works) |
||||
* - create Client Identifier? |
||||
* - only start requests if a valid local address is available on the netif |
||||
* - only start information requests if required (not for every RA) |
||||
* |
||||
* dhcp6_enable_stateful() enables stateful DHCPv6 for a netif (stateless disabled)<br> |
||||
* dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)<br> |
||||
* dhcp6_disable() disable DHCPv6 for a netif |
||||
* |
||||
* When enabled, requests are only issued after receipt of RA with the |
||||
* corresponding bits set. |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2018 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 <goldsimon@gmx.de> |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/dhcp6.h" |
||||
#include "lwip/prot/dhcp6.h" |
||||
#include "lwip/def.h" |
||||
#include "lwip/udp.h" |
||||
#include "lwip/dns.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#ifdef LWIP_HOOK_FILENAME |
||||
#include LWIP_HOOK_FILENAME |
||||
#endif |
||||
#ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS |
||||
#define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len) |
||||
#endif |
||||
#ifndef LWIP_HOOK_DHCP6_PARSE_OPTION |
||||
#define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0) |
||||
#endif |
||||
|
||||
#if LWIP_DNS && LWIP_DHCP6_MAX_DNS_SERVERS |
||||
#if DNS_MAX_SERVERS > LWIP_DHCP6_MAX_DNS_SERVERS |
||||
#define LWIP_DHCP6_PROVIDE_DNS_SERVERS LWIP_DHCP6_MAX_DNS_SERVERS |
||||
#else |
||||
#define LWIP_DHCP6_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS |
||||
#endif |
||||
#else |
||||
#define LWIP_DHCP6_PROVIDE_DNS_SERVERS 0 |
||||
#endif |
||||
|
||||
|
||||
/** Option handling: options are parsed in dhcp6_parse_reply
|
||||
* and saved in an array where other functions can load them from. |
||||
* This might be moved into the struct dhcp6 (not necessarily since |
||||
* lwIP is single-threaded and the array is only used while in recv |
||||
* callback). */ |
||||
enum dhcp6_option_idx { |
||||
DHCP6_OPTION_IDX_CLI_ID = 0, |
||||
DHCP6_OPTION_IDX_SERVER_ID, |
||||
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS |
||||
DHCP6_OPTION_IDX_DNS_SERVER, |
||||
DHCP6_OPTION_IDX_DOMAIN_LIST, |
||||
#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */ |
||||
#if LWIP_DHCP6_GET_NTP_SRV |
||||
DHCP6_OPTION_IDX_NTP_SERVER, |
||||
#endif /* LWIP_DHCP_GET_NTP_SRV */ |
||||
DHCP6_OPTION_IDX_MAX |
||||
}; |
||||
|
||||
struct dhcp6_option_info { |
||||
u8_t option_given; |
||||
u16_t val_start; |
||||
u16_t val_length; |
||||
}; |
||||
|
||||
/** Holds the decoded option info, only valid while in dhcp6_recv. */ |
||||
struct dhcp6_option_info dhcp6_rx_options[DHCP6_OPTION_IDX_MAX]; |
||||
|
||||
#define dhcp6_option_given(dhcp6, idx) (dhcp6_rx_options[idx].option_given != 0) |
||||
#define dhcp6_got_option(dhcp6, idx) (dhcp6_rx_options[idx].option_given = 1) |
||||
#define dhcp6_clear_option(dhcp6, idx) (dhcp6_rx_options[idx].option_given = 0) |
||||
#define dhcp6_clear_all_options(dhcp6) (memset(dhcp6_rx_options, 0, sizeof(dhcp6_rx_options))) |
||||
#define dhcp6_get_option_start(dhcp6, idx) (dhcp6_rx_options[idx].val_start) |
||||
#define dhcp6_get_option_length(dhcp6, idx) (dhcp6_rx_options[idx].val_length) |
||||
#define dhcp6_set_option(dhcp6, idx, start, len) do { dhcp6_rx_options[idx].val_start = (start); dhcp6_rx_options[idx].val_length = (len); }while(0) |
||||
|
||||
|
||||
const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002); |
||||
const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003); |
||||
|
||||
static struct udp_pcb *dhcp6_pcb; |
||||
static u8_t dhcp6_pcb_refcount; |
||||
|
||||
|
||||
/* receive, unfold, parse and free incoming messages */ |
||||
static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port); |
||||
|
||||
/** Ensure DHCP PCB is allocated and bound */ |
||||
static err_t |
||||
dhcp6_inc_pcb_refcount(void) |
||||
{ |
||||
if (dhcp6_pcb_refcount == 0) { |
||||
LWIP_ASSERT("dhcp6_inc_pcb_refcount(): memory leak", dhcp6_pcb == NULL); |
||||
|
||||
/* allocate UDP PCB */ |
||||
dhcp6_pcb = udp_new_ip6(); |
||||
|
||||
if (dhcp6_pcb == NULL) { |
||||
return ERR_MEM; |
||||
} |
||||
|
||||
ip_set_option(dhcp6_pcb, SOF_BROADCAST); |
||||
|
||||
/* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */ |
||||
udp_bind(dhcp6_pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT); |
||||
udp_recv(dhcp6_pcb, dhcp6_recv, NULL); |
||||
} |
||||
|
||||
dhcp6_pcb_refcount++; |
||||
|
||||
return ERR_OK; |
||||
} |
||||
|
||||
/** Free DHCP PCB if the last netif stops using it */ |
||||
static void |
||||
dhcp6_dec_pcb_refcount(void) |
||||
{ |
||||
LWIP_ASSERT("dhcp6_pcb_refcount(): refcount error", (dhcp6_pcb_refcount > 0)); |
||||
dhcp6_pcb_refcount--; |
||||
|
||||
if (dhcp6_pcb_refcount == 0) { |
||||
udp_remove(dhcp6_pcb); |
||||
dhcp6_pcb = NULL; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup dhcp6 |
||||
* Set a statically allocated struct dhcp6 to work with. |
||||
* Using this prevents dhcp6_start to allocate it using mem_malloc. |
||||
* |
||||
* @param netif the netif for which to set the struct dhcp |
||||
* @param dhcp6 (uninitialised) dhcp6 struct allocated by the application |
||||
*/ |
||||
void |
||||
dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6) |
||||
{ |
||||
LWIP_ASSERT("netif != NULL", netif != NULL); |
||||
LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL); |
||||
LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL); |
||||
|
||||
/* clear data structure */ |
||||
memset(dhcp6, 0, sizeof(struct dhcp6)); |
||||
/* dhcp6_set_state(&dhcp, DHCP6_STATE_OFF); */ |
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup dhcp6 |
||||
* Removes a struct dhcp6 from a netif. |
||||
* |
||||
* ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the |
||||
* struct dhcp6 since the memory is passed back to the heap. |
||||
* |
||||
* @param netif the netif from which to remove the struct dhcp |
||||
*/ |
||||
void dhcp6_cleanup(struct netif *netif) |
||||
{ |
||||
LWIP_ASSERT("netif != NULL", netif != NULL); |
||||
|
||||
if (netif_dhcp6_data(netif) != NULL) { |
||||
mem_free(netif_dhcp6_data(netif)); |
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL); |
||||
} |
||||
} |
||||
|
||||
static struct dhcp6* |
||||
dhcp6_get_struct(struct netif *netif, const char *dbg_requester) |
||||
{ |
||||
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif); |
||||
if (dhcp6 == NULL) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester)); |
||||
dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6)); |
||||
if (dhcp6 == NULL) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester)); |
||||
return NULL; |
||||
} |
||||
|
||||
/* clear data structure, this implies DHCP6_STATE_OFF */ |
||||
memset(dhcp6, 0, sizeof(struct dhcp6)); |
||||
/* store this dhcp6 client in the netif */ |
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6); |
||||
} else { |
||||
/* already has DHCP6 client attached */ |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester)); |
||||
} |
||||
|
||||
if (!dhcp6->pcb_allocated) { |
||||
if (dhcp6_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP6 PCB is allocated */ |
||||
mem_free(dhcp6); |
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL); |
||||
return NULL; |
||||
} |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6\n", dbg_requester)); |
||||
dhcp6->pcb_allocated = 1; |
||||
} |
||||
return dhcp6; |
||||
} |
||||
|
||||
/*
|
||||
* Set the DHCPv6 state |
||||
* If the state changed, reset the number of tries. |
||||
*/ |
||||
static void |
||||
dhcp6_set_state(struct dhcp6 *dhcp6, u8_t new_state, const char *dbg_caller) |
||||
{ |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("DHCPv6 state: %d -> %d (%s)\n", |
||||
dhcp6->state, new_state, dbg_caller)); |
||||
if (new_state != dhcp6->state) { |
||||
dhcp6->state = new_state; |
||||
dhcp6->tries = 0; |
||||
dhcp6->request_timeout = 0; |
||||
} |
||||
} |
||||
|
||||
static int |
||||
dhcp6_stateless_enabled(struct dhcp6 *dhcp6) |
||||
{ |
||||
if ((dhcp6->state == DHCP6_STATE_STATELESS_IDLE) || |
||||
(dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG)) { |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/*static int
|
||||
dhcp6_stateful_enabled(struct dhcp6 *dhcp6) |
||||
{ |
||||
if (dhcp6->state == DHCP6_STATE_OFF) { |
||||
return 0; |
||||
} |
||||
if (dhcp6_stateless_enabled(dhcp6)) { |
||||
return 0; |
||||
} |
||||
return 1; |
||||
}*/ |
||||
|
||||
/**
|
||||
* @ingroup dhcp6 |
||||
* Enable stateful DHCPv6 on this netif |
||||
* Requests are sent on receipt of an RA message with the |
||||
* ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set. |
||||
* |
||||
* A struct dhcp6 will be allocated for this netif if not |
||||
* set via @ref dhcp6_set_struct before. |
||||
* |
||||
* @todo: stateful DHCPv6 not supported, yet |
||||
*/ |
||||
err_t |
||||
dhcp6_enable_stateful(struct netif *netif) |
||||
{ |
||||
LWIP_UNUSED_ARG(netif); |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("stateful dhcp6 not implemented yet\n")); |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup dhcp6 |
||||
* Enable stateless DHCPv6 on this netif |
||||
* Requests are sent on receipt of an RA message with the |
||||
* ND6_RA_FLAG_OTHER_CONFIG flag set. |
||||
* |
||||
* A struct dhcp6 will be allocated for this netif if not |
||||
* set via @ref dhcp6_set_struct before. |
||||
*/ |
||||
err_t |
||||
dhcp6_enable_stateless(struct netif *netif) |
||||
{ |
||||
struct dhcp6 *dhcp6; |
||||
|
||||
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_enable_stateless(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); |
||||
|
||||
dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateless()"); |
||||
if (dhcp6 == NULL) { |
||||
return ERR_MEM; |
||||
} |
||||
if (dhcp6_stateless_enabled(dhcp6)) { |
||||
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 already enabled\n")); |
||||
return ERR_OK; |
||||
} else if (dhcp6->state != DHCP6_STATE_OFF) { |
||||
/* stateful running */ |
||||
/* @todo: stop stateful once it is implemented */ |
||||
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): switching from stateful to stateless DHCPv6\n")); |
||||
} |
||||
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 enabled\n")); |
||||
dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_enable_stateless"); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup dhcp6 |
||||
* Disable stateful or stateless DHCPv6 on this netif |
||||
* Requests are sent on receipt of an RA message with the |
||||
* ND6_RA_FLAG_OTHER_CONFIG flag set. |
||||
*/ |
||||
void |
||||
dhcp6_disable(struct netif *netif) |
||||
{ |
||||
struct dhcp6 *dhcp6; |
||||
|
||||
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_disable(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); |
||||
|
||||
dhcp6 = netif_dhcp6_data(netif); |
||||
if (dhcp6 != NULL) { |
||||
if (dhcp6->state != DHCP6_STATE_OFF) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_disable(): DHCPv6 disabled (old state: %s)\n", |
||||
(dhcp6_stateless_enabled(dhcp6) ? "stateless" : "stateful"))); |
||||
dhcp6_set_state(dhcp6, DHCP6_STATE_OFF, "dhcp6_disable"); |
||||
if (dhcp6->pcb_allocated != 0) { |
||||
dhcp6_dec_pcb_refcount(); /* free DHCPv6 PCB if not needed any more */ |
||||
dhcp6->pcb_allocated = 0; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Create a DHCPv6 request, fill in common headers |
||||
* |
||||
* @param netif the netif under DHCPv6 control |
||||
* @param dhcp6 dhcp6 control struct |
||||
* @param message_type message type of the request |
||||
* @param opt_len_alloc option length to allocate |
||||
* @param options_out_len option length on exit |
||||
* @return a pbuf for the message |
||||
*/ |
||||
static struct pbuf * |
||||
dhcp6_create_msg(struct netif *netif, struct dhcp6 *dhcp6, u8_t message_type, |
||||
u16_t opt_len_alloc, u16_t *options_out_len) |
||||
{ |
||||
struct pbuf *p_out; |
||||
struct dhcp6_msg *msg_out; |
||||
|
||||
LWIP_ERROR("dhcp6_create_msg: netif != NULL", (netif != NULL), return NULL;); |
||||
LWIP_ERROR("dhcp6_create_msg: dhcp6 != NULL", (dhcp6 != NULL), return NULL;); |
||||
p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp6_msg) + opt_len_alloc, PBUF_RAM); |
||||
if (p_out == NULL) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, |
||||
("dhcp6_create_msg(): could not allocate pbuf\n")); |
||||
return NULL; |
||||
} |
||||
LWIP_ASSERT("dhcp6_create_msg: check that first pbuf can hold struct dhcp6_msg", |
||||
(p_out->len >= sizeof(struct dhcp6_msg) + opt_len_alloc)); |
||||
|
||||
/* @todo: limit new xid for certain message types? */ |
||||
/* reuse transaction identifier in retransmissions */ |
||||
if (dhcp6->tries == 0) { |
||||
dhcp6->xid = LWIP_RAND() & 0xFFFFFF; |
||||
} |
||||
|
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, |
||||
("transaction id xid(%"X32_F")\n", dhcp6->xid)); |
||||
|
||||
msg_out = (struct dhcp6_msg *)p_out->payload; |
||||
memset(msg_out, 0, sizeof(struct dhcp6_msg) + opt_len_alloc); |
||||
|
||||
msg_out->msgtype = message_type; |
||||
msg_out->transaction_id[0] = (u8_t)(dhcp6->xid >> 16); |
||||
msg_out->transaction_id[1] = (u8_t)(dhcp6->xid >> 8); |
||||
msg_out->transaction_id[2] = (u8_t)dhcp6->xid; |
||||
*options_out_len = 0; |
||||
return p_out; |
||||
} |
||||
|
||||
static u16_t |
||||
dhcp6_option_short(u16_t options_out_len, u8_t *options, u16_t value) |
||||
{ |
||||
options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8); |
||||
options[options_out_len++] = (u8_t) (value & 0x00ffU); |
||||
return options_out_len; |
||||
} |
||||
|
||||
static u16_t |
||||
dhcp6_option_optionrequest(u16_t options_out_len, u8_t *options, const u16_t *req_options, |
||||
u16_t num_req_options, u16_t max_len) |
||||
{ |
||||
size_t i; |
||||
u16_t ret; |
||||
|
||||
LWIP_ASSERT("dhcp6_option_optionrequest: options_out_len + sizeof(struct dhcp6_msg) + addlen <= max_len", |
||||
sizeof(struct dhcp6_msg) + options_out_len + 4U + (2U * num_req_options) <= max_len); |
||||
LWIP_UNUSED_ARG(max_len); |
||||
|
||||
ret = dhcp6_option_short(options_out_len, options, DHCP6_OPTION_ORO); |
||||
ret = dhcp6_option_short(ret, options, 2 * num_req_options); |
||||
for (i = 0; i < num_req_options; i++) { |
||||
ret = dhcp6_option_short(ret, options, req_options[i]); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/* All options are added, shrink the pbuf to the required size */ |
||||
static void |
||||
dhcp6_msg_finalize(u16_t options_out_len, struct pbuf *p_out) |
||||
{ |
||||
/* shrink the pbuf to the actual content length */ |
||||
pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp6_msg) + options_out_len)); |
||||
} |
||||
|
||||
|
||||
#if LWIP_IPV6_DHCP6_STATELESS |
||||
static void |
||||
dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6) |
||||
{ |
||||
const u16_t requested_options[] = { |
||||
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS |
||||
DHCP6_OPTION_DNS_SERVERS, |
||||
DHCP6_OPTION_DOMAIN_LIST |
||||
#endif |
||||
#if LWIP_DHCP6_GET_NTP_SRV |
||||
, DHCP6_OPTION_SNTP_SERVERS |
||||
#endif |
||||
}; |
||||
|
||||
u16_t msecs; |
||||
struct pbuf *p_out; |
||||
u16_t options_out_len; |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n")); |
||||
/* create and initialize the DHCP message header */ |
||||
p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST, 4 + sizeof(requested_options), &options_out_len); |
||||
if (p_out != NULL) { |
||||
err_t err; |
||||
struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload; |
||||
u8_t *options = (u8_t *)(msg_out + 1); |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n")); |
||||
|
||||
options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options, |
||||
LWIP_ARRAYSIZE(requested_options), p_out->len); |
||||
LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out, |
||||
DHCP6_INFOREQUEST, options_out_len, p_out->len); |
||||
dhcp6_msg_finalize(options_out_len, p_out); |
||||
|
||||
err = udp_sendto_if(dhcp6_pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif); |
||||
pbuf_free(p_out); |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err)); |
||||
LWIP_UNUSED_ARG(err); |
||||
} else { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n")); |
||||
} |
||||
dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request"); |
||||
if (dhcp6->tries < 255) { |
||||
dhcp6->tries++; |
||||
} |
||||
msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000); |
||||
dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS); |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs)); |
||||
} |
||||
|
||||
static err_t |
||||
dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6) |
||||
{ |
||||
/* stateless mode enabled and no request running? */ |
||||
if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) { |
||||
/* send Information-request and wait for answer; setup receive timeout */ |
||||
dhcp6_information_request(netif, dhcp6); |
||||
} |
||||
|
||||
return ERR_OK; |
||||
} |
||||
|
||||
static void |
||||
dhcp6_abort_config_request(struct dhcp6 *dhcp6) |
||||
{ |
||||
if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) { |
||||
/* abort running request */ |
||||
dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request"); |
||||
} |
||||
} |
||||
|
||||
/* Handle a REPLY to INFOREQUEST
|
||||
* This parses DNS and NTP server addresses from the reply. |
||||
*/ |
||||
static void |
||||
dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in) |
||||
{ |
||||
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif); |
||||
|
||||
LWIP_UNUSED_ARG(dhcp6); |
||||
LWIP_UNUSED_ARG(p_msg_in); |
||||
|
||||
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS |
||||
if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) { |
||||
ip_addr_t dns_addr; |
||||
ip6_addr_t *dns_addr6; |
||||
u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER); |
||||
u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER); |
||||
u16_t idx; |
||||
u8_t n; |
||||
|
||||
ip_addr_set_zero_ip6(&dns_addr); |
||||
dns_addr6 = ip_2_ip6(&dns_addr); |
||||
for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS); |
||||
n++, idx += sizeof(struct ip6_addr_packed)) { |
||||
u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx); |
||||
if (copied != sizeof(struct ip6_addr_packed)) { |
||||
/* pbuf length mismatch */ |
||||
return; |
||||
} |
||||
ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif); |
||||
/* @todo: do we need a different offset than DHCP(v4)? */ |
||||
dns_setserver(n, &dns_addr); |
||||
} |
||||
} |
||||
/* @ todo: parse and set Domain Search List */ |
||||
#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */ |
||||
|
||||
#if LWIP_DHCP6_GET_NTP_SRV |
||||
if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) { |
||||
ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS]; |
||||
u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER); |
||||
u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER); |
||||
u16_t idx; |
||||
u8_t n; |
||||
|
||||
for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS); |
||||
n++, idx += sizeof(struct ip6_addr_packed)) { |
||||
u16_t copied; |
||||
ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]); |
||||
ip_addr_set_zero_ip6(&ntp_server_addrs[n]); |
||||
copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx); |
||||
if (copied != sizeof(struct ip6_addr_packed)) { |
||||
/* pbuf length mismatch */ |
||||
return; |
||||
} |
||||
ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif); |
||||
} |
||||
dhcp6_set_ntp_servers(n, ntp_server_addrs); |
||||
} |
||||
#endif /* LWIP_DHCP6_GET_NTP_SRV */ |
||||
} |
||||
#endif /* LWIP_IPV6_DHCP6_STATELESS */ |
||||
|
||||
/** This function is called from nd6 module when an RA message is received
|
||||
* It triggers DHCPv6 requests (if enabled). |
||||
*/ |
||||
void |
||||
dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config) |
||||
{ |
||||
struct dhcp6 *dhcp6; |
||||
|
||||
LWIP_ASSERT("netif != NULL", netif != NULL); |
||||
dhcp6 = netif_dhcp6_data(netif); |
||||
|
||||
LWIP_UNUSED_ARG(managed_addr_config); |
||||
LWIP_UNUSED_ARG(other_config); |
||||
LWIP_UNUSED_ARG(dhcp6); |
||||
|
||||
#if LWIP_IPV6_DHCP6_STATELESS |
||||
if (dhcp6 != NULL) { |
||||
if (dhcp6_stateless_enabled(dhcp6)) { |
||||
if (other_config) { |
||||
dhcp6_request_config(netif, dhcp6); |
||||
} else { |
||||
dhcp6_abort_config_request(dhcp6); |
||||
} |
||||
} |
||||
} |
||||
#endif /* LWIP_IPV6_DHCP6_STATELESS */ |
||||
} |
||||
|
||||
/**
|
||||
* Parse the DHCPv6 message and extract the DHCPv6 options. |
||||
* |
||||
* Extract the DHCPv6 options (offset + length) so that we can later easily |
||||
* check for them or extract the contents. |
||||
*/ |
||||
static err_t |
||||
dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6) |
||||
{ |
||||
u16_t offset; |
||||
u16_t offset_max; |
||||
u16_t options_idx; |
||||
struct dhcp6_msg *msg_in; |
||||
|
||||
LWIP_UNUSED_ARG(dhcp6); |
||||
|
||||
/* clear received options */ |
||||
dhcp6_clear_all_options(dhcp6); |
||||
msg_in = (struct dhcp6_msg *)p->payload; |
||||
|
||||
/* parse options */ |
||||
|
||||
options_idx = sizeof(struct dhcp6_msg); |
||||
/* parse options to the end of the received packet */ |
||||
offset_max = p->tot_len; |
||||
|
||||
offset = options_idx; |
||||
/* at least 4 byte to read? */ |
||||
while ((offset + 4 <= offset_max)) { |
||||
u8_t op_len_buf[4]; |
||||
u8_t *op_len; |
||||
u16_t op; |
||||
u16_t len; |
||||
u16_t val_offset = (u16_t)(offset + 4); |
||||
if (val_offset < offset) { |
||||
/* overflow */ |
||||
return ERR_BUF; |
||||
} |
||||
/* copy option + length, might be split across pbufs */ |
||||
op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, 4, 4, offset); |
||||
if (op_len == NULL) { |
||||
/* failed to get option and length */ |
||||
return ERR_VAL; |
||||
} |
||||
op = (op_len[0] << 8) | op_len[1]; |
||||
len = (op_len[2] << 8) | op_len[3]; |
||||
offset = val_offset + len; |
||||
if (offset < val_offset) { |
||||
/* overflow */ |
||||
return ERR_BUF; |
||||
} |
||||
|
||||
switch (op) { |
||||
case (DHCP6_OPTION_CLIENTID): |
||||
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID); |
||||
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len); |
||||
break; |
||||
case (DHCP6_OPTION_SERVERID): |
||||
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID); |
||||
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len); |
||||
break; |
||||
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS |
||||
case (DHCP6_OPTION_DNS_SERVERS): |
||||
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER); |
||||
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len); |
||||
break; |
||||
case (DHCP6_OPTION_DOMAIN_LIST): |
||||
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST); |
||||
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len); |
||||
break; |
||||
#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */ |
||||
#if LWIP_DHCP6_GET_NTP_SRV |
||||
case (DHCP6_OPTION_SNTP_SERVERS): |
||||
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER); |
||||
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len); |
||||
break; |
||||
#endif /* LWIP_DHCP6_GET_NTP_SRV*/ |
||||
default: |
||||
LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op)); |
||||
LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in, |
||||
msg_in->msgtype, op, len, q, val_offset); |
||||
break; |
||||
} |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static void |
||||
dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) |
||||
{ |
||||
struct netif *netif = ip_current_input_netif(); |
||||
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif); |
||||
struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload; |
||||
u8_t msg_type; |
||||
u32_t xid; |
||||
|
||||
LWIP_UNUSED_ARG(arg); |
||||
|
||||
/* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */ |
||||
if ((dhcp6 == NULL) || (dhcp6->pcb_allocated == 0)) { |
||||
goto free_pbuf_and_return; |
||||
} |
||||
|
||||
LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;); |
||||
|
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p, |
||||
ipaddr_ntoa(addr), port)); |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); |
||||
/* prevent warnings about unused arguments */ |
||||
LWIP_UNUSED_ARG(pcb); |
||||
LWIP_UNUSED_ARG(addr); |
||||
LWIP_UNUSED_ARG(port); |
||||
|
||||
if (p->len < sizeof(struct dhcp6_msg)) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n")); |
||||
goto free_pbuf_and_return; |
||||
} |
||||
|
||||
/* match transaction ID against what we expected */ |
||||
xid = reply_msg->transaction_id[0] << 16; |
||||
xid |= reply_msg->transaction_id[1] << 8; |
||||
xid |= reply_msg->transaction_id[2]; |
||||
if (xid != dhcp6->xid) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, |
||||
("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid)); |
||||
goto free_pbuf_and_return; |
||||
} |
||||
/* option fields could be unfold? */ |
||||
if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, |
||||
("problem unfolding DHCPv6 message - too short on memory?\n")); |
||||
goto free_pbuf_and_return; |
||||
} |
||||
|
||||
/* read DHCP message type */ |
||||
msg_type = reply_msg->msgtype; |
||||
/* message type is DHCP6 REPLY? */ |
||||
if (msg_type == DHCP6_REPLY) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n")); |
||||
#if LWIP_IPV6_DHCP6_STATELESS |
||||
/* in info-requesting state? */ |
||||
if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) { |
||||
dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv"); |
||||
dhcp6_handle_config_reply(netif, p); |
||||
} else |
||||
#endif /* LWIP_IPV6_DHCP6_STATELESS */ |
||||
{ |
||||
/* @todo: handle reply in other states? */ |
||||
} |
||||
} else { |
||||
/* @todo: handle other message types */ |
||||
} |
||||
|
||||
free_pbuf_and_return: |
||||
pbuf_free(p); |
||||
} |
||||
|
||||
/**
|
||||
* A DHCPv6 request has timed out. |
||||
* |
||||
* The timer that was started with the DHCPv6 request has |
||||
* timed out, indicating no response was received in time. |
||||
*/ |
||||
static void |
||||
dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6) |
||||
{ |
||||
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n")); |
||||
|
||||
LWIP_UNUSED_ARG(netif); |
||||
LWIP_UNUSED_ARG(dhcp6); |
||||
|
||||
#if LWIP_IPV6_DHCP6_STATELESS |
||||
/* back-off period has passed, or server selection timed out */ |
||||
if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) { |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n")); |
||||
dhcp6_information_request(netif, dhcp6); |
||||
} |
||||
#endif /* LWIP_IPV6_DHCP6_STATELESS */ |
||||
} |
||||
|
||||
/**
|
||||
* DHCPv6 timeout handling (this function must be called every 500ms, |
||||
* see @ref DHCP6_TIMER_MSECS). |
||||
* |
||||
* A DHCPv6 server is expected to respond within a short period of time. |
||||
* This timer checks whether an outstanding DHCPv6 request is timed out. |
||||
*/ |
||||
void |
||||
dhcp6_tmr(void) |
||||
{ |
||||
struct netif *netif; |
||||
/* loop through netif's */ |
||||
NETIF_FOREACH(netif) { |
||||
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif); |
||||
/* only act on DHCPv6 configured interfaces */ |
||||
if (dhcp6 != NULL) { |
||||
/* timer is active (non zero), and is about to trigger now */ |
||||
if (dhcp6->request_timeout > 1) { |
||||
dhcp6->request_timeout--; |
||||
} else if (dhcp6->request_timeout == 1) { |
||||
dhcp6->request_timeout--; |
||||
/* { dhcp6->request_timeout == 0 } */ |
||||
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n")); |
||||
/* this client's request timeout triggered */ |
||||
dhcp6_timeout(netif, dhcp6); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */ |
||||
@ -1,123 +0,0 @@
|
||||
/**
|
||||
* @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" |
||||
|
||||
#if LWIP_IPV6 && LWIP_ETHERNET |
||||
|
||||
#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 "lwip/prot/ethernet.h" |
||||
#include "netif/ethernet.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
/**
|
||||
* 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, ask the ND6 module what to do. It will either let us |
||||
* send the the packet right away, or queue the packet for later itself, unless |
||||
* an error occurs. |
||||
* |
||||
* @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_OK or the return value of @ref nd6_get_next_hop_addr_or_queue. |
||||
*/ |
||||
err_t |
||||
ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) |
||||
{ |
||||
struct eth_addr dest; |
||||
const u8_t *hwaddr; |
||||
err_t result; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* The destination IP address must be properly zoned from here on down. */ |
||||
IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif); |
||||
|
||||
/* 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 ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6); |
||||
} |
||||
|
||||
/* We have a unicast destination IP address */ |
||||
/* @todo anycast? */ |
||||
|
||||
/* Ask ND6 what to do with the packet. */ |
||||
result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr); |
||||
if (result != ERR_OK) { |
||||
return result; |
||||
} |
||||
|
||||
/* If no hardware address is returned, nd6 has queued the packet for later. */ |
||||
if (hwaddr == NULL) { |
||||
return ERR_OK; |
||||
} |
||||
|
||||
/* Send out the packet using the returned hardware address. */ |
||||
SMEMCPY(dest.addr, hwaddr, 6); |
||||
return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6); |
||||
} |
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_ETHERNET */ |
||||
@ -1,425 +0,0 @@
|
||||
/**
|
||||
* @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/prot/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> |
||||
|
||||
#if !LWIP_ICMP6_DATASIZE || (LWIP_ICMP6_DATASIZE > (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN)) |
||||
#undef LWIP_ICMP6_DATASIZE |
||||
#define LWIP_ICMP6_DATASIZE (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN) |
||||
#endif |
||||
|
||||
/* Forward declarations */ |
||||
static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); |
||||
static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, |
||||
u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr); |
||||
static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, |
||||
u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif); |
||||
|
||||
|
||||
/**
|
||||
* 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; |
||||
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; |
||||
#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. |
||||
* |
||||
* This function must be used only in direct response to a packet that is being |
||||
* received right now. Otherwise, address zones would be lost. |
||||
* |
||||
* @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. |
||||
* |
||||
* This function must be used only in direct response to a packet that is being |
||||
* received right now. Otherwise, address zones would be lost. |
||||
* |
||||
* @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. |
||||
* |
||||
* This function must be used only in direct response to a packet that is being |
||||
* received right now. Otherwise, address zones would be lost. |
||||
* |
||||
* @param p the input packet for which the 'time exceeded' 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 'time exceeded' packet, with explicit source and destination |
||||
* addresses. |
||||
* |
||||
* This function may be used to send a response sometime after receiving the |
||||
* packet for which this response is meant. The provided source and destination |
||||
* addresses are used primarily to retain their zone information. |
||||
* |
||||
* @param p the input packet for which the 'time exceeded' should be sent, |
||||
* p->payload pointing to the IPv6 header |
||||
* @param c ICMPv6 code for the time exceeded type |
||||
* @param src_addr source address of the original packet, with zone information |
||||
* @param dest_addr destination address of the original packet, with zone |
||||
* information |
||||
*/ |
||||
void |
||||
icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c, |
||||
const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) |
||||
{ |
||||
icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr); |
||||
} |
||||
|
||||
/**
|
||||
* Send an icmpv6 'parameter problem' packet. |
||||
* |
||||
* This function must be used only in direct response to a packet that is being |
||||
* received right now. Otherwise, address zones would be lost and the calculated |
||||
* offset would be wrong (calculated against ip6_current_header()). |
||||
* |
||||
* @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, const void *pointer) |
||||
{ |
||||
u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header()); |
||||
icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP); |
||||
} |
||||
|
||||
/**
|
||||
* Send an ICMPv6 packet in response to an incoming packet. |
||||
* The packet is sent *to* ip_current_src_addr() on ip_current_netif(). |
||||
* |
||||
* @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) |
||||
{ |
||||
const struct ip6_addr *reply_src, *reply_dest; |
||||
struct netif *netif = ip_current_netif(); |
||||
|
||||
LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL); |
||||
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) { |
||||
ICMP6_STATS_INC(icmp6.rterr); |
||||
return; |
||||
} |
||||
icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif); |
||||
} |
||||
|
||||
/**
|
||||
* Send an ICMPv6 packet in response to an incoming packet. |
||||
* |
||||
* Call this function if the packet is NOT sent as a direct response to an |
||||
* incoming packet, but rather sometime later (e.g. for a fragment reassembly |
||||
* timeout). The caller must provide the zoned source and destination addresses |
||||
* from the original packet with the src_addr and dest_addr parameters. The |
||||
* reason for this approach is that while the addresses themselves are part of |
||||
* the original packet, their zone information is not, thus possibly resulting |
||||
* in a link-local response being sent over the wrong link. |
||||
* |
||||
* @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 |
||||
* @param src_addr original source address |
||||
* @param dest_addr original destination address |
||||
*/ |
||||
static void |
||||
icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type, |
||||
const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) |
||||
{ |
||||
const struct ip6_addr *reply_src, *reply_dest; |
||||
struct netif *netif; |
||||
|
||||
/* Get the destination address and netif for this ICMP message. */ |
||||
LWIP_ASSERT("must provide both source and destination", src_addr != NULL); |
||||
LWIP_ASSERT("must provide both source and destination", dest_addr != NULL); |
||||
|
||||
/* Special case, as ip6_current_xxx is either NULL, or points
|
||||
to a different packet than the one that expired. */ |
||||
IP6_ADDR_ZONECHECK(src_addr); |
||||
IP6_ADDR_ZONECHECK(dest_addr); |
||||
/* Swap source and destination for the reply. */ |
||||
reply_dest = src_addr; |
||||
reply_src = dest_addr; |
||||
netif = ip6_route(reply_src, reply_dest); |
||||
if (netif == NULL) { |
||||
ICMP6_STATS_INC(icmp6.rterr); |
||||
return; |
||||
} |
||||
icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, |
||||
reply_dest, netif); |
||||
} |
||||
|
||||
/**
|
||||
* Send an ICMPv6 packet (with srd/dst address and netif given). |
||||
* |
||||
* @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 |
||||
* @param reply_src source address of the packet to send |
||||
* @param reply_dest destination address of the packet to send |
||||
* @param netif netif to send the packet |
||||
*/ |
||||
static void |
||||
icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type, |
||||
const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif) |
||||
{ |
||||
struct pbuf *q; |
||||
struct icmp6_hdr *icmp6hdr; |
||||
u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE); |
||||
|
||||
/* ICMPv6 header + datalen (as much of the offending packet as possible) */ |
||||
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + datalen, |
||||
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 icmp6 header", |
||||
(q->len >= (sizeof(struct icmp6_hdr)))); |
||||
|
||||
icmp6hdr = (struct icmp6_hdr *)q->payload; |
||||
icmp6hdr->type = type; |
||||
icmp6hdr->code = code; |
||||
icmp6hdr->data = lwip_htonl(data); |
||||
|
||||
/* copy fields from original packet */ |
||||
pbuf_copy_partial_pbuf(q, p, datalen, sizeof(struct icmp6_hdr)); |
||||
|
||||
/* 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 */ |
||||
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* |
||||
* INET v6 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> |
||||
* |
||||
* |
||||
* Please coordinate changes and requests with Ivan Delamer |
||||
* <delamer@inicotech.com> |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/def.h" |
||||
#include "lwip/inet.h" |
||||
|
||||
/** This variable is initialized by the system to contain the wildcard IPv6 address.
|
||||
*/ |
||||
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; |
||||
|
||||
#endif /* LWIP_IPV6 */ |
||||
@ -1,355 +0,0 @@
|
||||
/**
|
||||
* @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" |
||||
#include "lwip/netif.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#if LWIP_IPV4 |
||||
#include "lwip/ip4_addr.h" /* for ip6addr_aton to handle IPv4-mapped addresses */ |
||||
#endif /* LWIP_IPV4 */ |
||||
|
||||
/* used by IP6_ADDR_ANY(6) in ip6_addr.h */ |
||||
const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul); |
||||
|
||||
#define lwip_xchar(i) ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10)) |
||||
|
||||
/**
|
||||
* 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; |
||||
#if LWIP_IPV4 |
||||
int check_ipv4_mapped = 0; |
||||
#endif /* LWIP_IPV4 */ |
||||
|
||||
/* 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--; |
||||
#if LWIP_IPV4 |
||||
} else if (*s == '.') { |
||||
if ((zero_blocks == 5) ||(zero_blocks == 2)) { |
||||
check_ipv4_mapped = 1; |
||||
/* last block could be the start of an IPv4 address */ |
||||
zero_blocks--; |
||||
} else { |
||||
/* invalid format */ |
||||
return 0; |
||||
} |
||||
break; |
||||
#endif /* LWIP_IPV4 */ |
||||
} else if (!lwip_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++; |
||||
#if LWIP_IPV4 |
||||
if (check_ipv4_mapped) { |
||||
if (current_block_index == 6) { |
||||
ip4_addr_t ip4; |
||||
int ret = ip4addr_aton(s + 1, &ip4); |
||||
if (ret) { |
||||
if (addr) { |
||||
addr->addr[3] = lwip_htonl(ip4.addr); |
||||
current_block_index++; |
||||
goto fix_byte_order_and_return; |
||||
} |
||||
return 1; |
||||
} |
||||
} |
||||
} |
||||
#endif /* LWIP_IPV4 */ |
||||
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 (lwip_isxdigit(*s)) { |
||||
/* add current digit */ |
||||
current_block_value = (current_block_value << 4) + |
||||
(lwip_isdigit(*s) ? (u32_t)(*s - '0') : |
||||
(u32_t)(10 + (lwip_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; |
||||
} |
||||
#if LWIP_IPV4 |
||||
fix_byte_order_and_return: |
||||
#endif |
||||
/* convert to network byte order. */ |
||||
for (addr_index = 0; addr_index < 4; addr_index++) { |
||||
addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]); |
||||
} |
||||
|
||||
ip6_addr_clear_zone(addr); |
||||
#if LWIP_IPV6_SCOPES |
||||
if (*s == '%') { |
||||
const char *scopestr = s + 1; |
||||
if (*scopestr) { |
||||
struct netif *netif = netif_find(scopestr); |
||||
if (netif) { |
||||
ip6_addr_assign_zone(addr, IP6_UNKNOWN, netif); |
||||
} |
||||
} |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
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; |
||||
|
||||
#if LWIP_IPV4 |
||||
if (ip6_addr_isipv4mappedipv6(addr)) { |
||||
/* This is an IPv4 mapped address */ |
||||
ip4_addr_t addr4; |
||||
char *ret; |
||||
#define IP4MAPPED_HEADER "::FFFF:" |
||||
char *buf_ip4 = buf + sizeof(IP4MAPPED_HEADER) - 1; |
||||
int buflen_ip4 = buflen - sizeof(IP4MAPPED_HEADER) + 1; |
||||
if (buflen < (int)sizeof(IP4MAPPED_HEADER)) { |
||||
return NULL; |
||||
} |
||||
memcpy(buf, IP4MAPPED_HEADER, sizeof(IP4MAPPED_HEADER)); |
||||
addr4.addr = addr->addr[3]; |
||||
ret = ip4addr_ntoa_r(&addr4, buf_ip4, buflen_ip4); |
||||
if (ret != buf_ip4) { |
||||
return NULL; |
||||
} |
||||
return buf; |
||||
} |
||||
#endif /* LWIP_IPV4 */ |
||||
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 = lwip_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 = lwip_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++] = lwip_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++] = lwip_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++] = lwip_xchar(((current_block_value & 0xf0) >> 4)); |
||||
zero_flag = 0; |
||||
if (i >= buflen) { |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
buf[i++] = lwip_xchar((current_block_value & 0xf)); |
||||
if (i >= buflen) { |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
buf[i] = 0; |
||||
|
||||
return buf; |
||||
} |
||||
|
||||
#endif /* LWIP_IPV6 */ |
||||
@ -1,876 +0,0 @@
|
||||
/**
|
||||
* @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 |
||||
/* The number of bytes we need to "borrow" from (i.e., overwrite in) the header
|
||||
* that precedes the fragment header for reassembly pruposes. */ |
||||
#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; |
||||
u16_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; |
||||
/* Restore the part that we've overwritten with our helper structure, or we
|
||||
* might send garbage (and disclose a pointer) in the ICMPv6 reply. */ |
||||
MEMCPY(p->payload, ipr->orig_hdr, sizeof(*iprh)); |
||||
/* 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*)ipr->iphdr))) { |
||||
LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed", 0); |
||||
} |
||||
else { |
||||
/* Reconstruct the zoned source and destination addresses, so that we do
|
||||
* not end up sending the ICMP response over the wrong link. */ |
||||
ip6_addr_t src_addr, dest_addr; |
||||
ip6_addr_copy_from_packed(src_addr, IPV6_FRAG_SRC(ipr)); |
||||
ip6_addr_set_zone(&src_addr, ipr->src_zone); |
||||
ip6_addr_copy_from_packed(dest_addr, IPV6_FRAG_DEST(ipr)); |
||||
ip6_addr_set_zone(&dest_addr, ipr->dest_zone); |
||||
/* Send the actual ICMP response. */ |
||||
icmp6_time_exceeded_with_addrs(p, ICMP6_TE_FRAG, &src_addr, &dest_addr); |
||||
} |
||||
clen = pbuf_clen(p); |
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); |
||||
pbufs_freed = (u16_t)(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 = (u16_t)(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 = (u16_t)(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, start, end; |
||||
ptrdiff_t hdrdiff; |
||||
u16_t clen; |
||||
u8_t valid = 1; |
||||
struct pbuf *q, *next_pbuf; |
||||
|
||||
IP6_FRAG_STATS_INC(ip6_frag.recv); |
||||
|
||||
/* ip6_frag_hdr must be in the first pbuf, not chained. Checked by caller. */ |
||||
LWIP_ASSERT("IPv6 fragment header does not fit in first pbuf", |
||||
p->len >= sizeof(struct ip6_frag_hdr)); |
||||
|
||||
frag_hdr = (struct ip6_frag_hdr *) p->payload; |
||||
|
||||
clen = pbuf_clen(p); |
||||
|
||||
offset = lwip_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 = lwip_ntohs(ip6_current_header()->_plen); |
||||
hdrdiff = (u8_t*)p->payload - (const u8_t*)ip6_current_header(); |
||||
LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff <= 0xFFFF); |
||||
LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff >= IP6_HLEN); |
||||
hdrdiff -= IP6_HLEN; |
||||
hdrdiff += IP6_FRAG_HLEN; |
||||
if (hdrdiff > len) { |
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr); |
||||
goto nullreturn; |
||||
} |
||||
len = (u16_t)(len - hdrdiff); |
||||
start = (offset & IP6_FRAG_OFFSET_MASK); |
||||
if (start > (0xFFFF - len)) { |
||||
/* u16_t overflow, cannot handle this */ |
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr); |
||||
goto nullreturn; |
||||
} |
||||
|
||||
/* Look for the datagram the fragment belongs to in the current datagram queue,
|
||||
* remembering the previous in the queue for later dequeueing. */ |
||||
for (ipr = reassdatagrams, ipr_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_packed_eq(ip6_current_src_addr(), &(IPV6_FRAG_SRC(ipr)), ipr->src_zone) && |
||||
ip6_addr_packed_eq(ip6_current_dest_addr(), &(IPV6_FRAG_DEST(ipr)), ipr->dest_zone)) { |
||||
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); |
||||
goto nullreturn; |
||||
} |
||||
} |
||||
|
||||
memset(ipr, 0, sizeof(struct ip6_reassdata)); |
||||
ipr->timer = IPV6_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). */ |
||||
/* need to use the none-const pointer here: */ |
||||
ipr->iphdr = ip_data.current_ip6_header; |
||||
#if IPV6_FRAG_COPYHEADER |
||||
MEMCPY(&ipr->src, &ip6_current_header()->src, sizeof(ipr->src)); |
||||
MEMCPY(&ipr->dest, &ip6_current_header()->dest, sizeof(ipr->dest)); |
||||
#endif /* IPV6_FRAG_COPYHEADER */ |
||||
#if LWIP_IPV6_SCOPES |
||||
/* Also store the address zone information.
|
||||
* @todo It is possible that due to netif destruction and recreation, the |
||||
* stored zones end up resolving to a different interface. In that case, we |
||||
* risk sending a "time exceeded" ICMP response over the wrong link. |
||||
* Ideally, netif destruction would clean up matching pending reassembly |
||||
* structures, but custom zone mappings would make that non-trivial. */ |
||||
ipr->src_zone = ip6_addr_zone(ip6_current_src_addr()); |
||||
ipr->dest_zone = ip6_addr_zone(ip6_current_dest_addr()); |
||||
#endif /* LWIP_IPV6_SCOPES */ |
||||
/* 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); |
||||
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. */ |
||||
u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM); |
||||
LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ |
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); |
||||
} |
||||
#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 */ |
||||
|
||||
/* Prepare the pointer to the helper structure, and its initial values.
|
||||
* Do not yet write to the structure itself, as we still have to make a |
||||
* backup of the original data, and we should not do that until we know for |
||||
* sure that we are going to add this packet to the list. */ |
||||
iprh = (struct ip6_reass_helper *)p->payload; |
||||
next_pbuf = NULL; |
||||
end = (u16_t)(start + 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 (start < iprh_tmp->start) { |
||||
#if IP_REASS_CHECK_OVERLAP |
||||
if (end > iprh_tmp->start) { |
||||
/* fragment overlaps with following, throw away */ |
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr); |
||||
goto nullreturn; |
||||
} |
||||
if (iprh_prev != NULL) { |
||||
if (start < iprh_prev->end) { |
||||
/* fragment overlaps with previous, throw away */ |
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr); |
||||
goto nullreturn; |
||||
} |
||||
} |
||||
#endif /* IP_REASS_CHECK_OVERLAP */ |
||||
/* Check if the fragments received so far have no gaps. */ |
||||
if (iprh_prev != NULL) { |
||||
if (iprh_prev->end != start) { |
||||
/* There is a fragment missing between the current
|
||||
* and the previous fragment */ |
||||
valid = 0; |
||||
} |
||||
} |
||||
if (end != iprh_tmp->start) { |
||||
/* There is a fragment missing between the current
|
||||
* and the following fragment */ |
||||
valid = 0; |
||||
} |
||||
/* the new pbuf should be inserted before this */ |
||||
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 (start == iprh_tmp->start) { |
||||
/* received the same datagram twice: no need to keep the datagram */ |
||||
goto nullreturn; |
||||
#if IP_REASS_CHECK_OVERLAP |
||||
} else if (start < iprh_tmp->end) { |
||||
/* overlap: no need to keep the new datagram */ |
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr); |
||||
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 <= start); |
||||
#endif /* IP_REASS_CHECK_OVERLAP */ |
||||
iprh_prev->next_pbuf = p; |
||||
if (iprh_prev->end != 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 = (u16_t)(ip6_reass_pbufcount + clen); |
||||
|
||||
/* Remember IPv6 header if this is the first fragment. */ |
||||
if (start == 0) { |
||||
/* need to use the none-const pointer here: */ |
||||
ipr->iphdr = ip_data.current_ip6_header; |
||||
/* Make a backup of the part of the packet data that we are about to
|
||||
* overwrite, so that we can restore the original later. */ |
||||
MEMCPY(ipr->orig_hdr, p->payload, sizeof(*iprh)); |
||||
/* For IPV6_FRAG_COPYHEADER there is no need to copy src/dst again, as they
|
||||
* will be the same as they were. With LWIP_IPV6_SCOPES, the same applies |
||||
* to the source/destination zones. */ |
||||
} |
||||
/* Only after the backup do we get to fill in the actual helper structure. */ |
||||
iprh->next_pbuf = next_pbuf; |
||||
iprh->start = start; |
||||
iprh->end = end; |
||||
|
||||
/* 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) { |
||||
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_remove_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 */ |
||||
u8_t hdrerr = pbuf_remove_header(next_pbuf, IPV6_FRAG_REQROOM); |
||||
LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ |
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); |
||||
} |
||||
#endif |
||||
pbuf_cat(ipr->p, next_pbuf); |
||||
} |
||||
else { |
||||
iprh_tmp = NULL; |
||||
} |
||||
|
||||
iprh = iprh_tmp; |
||||
} |
||||
|
||||
/* Get the first pbuf. */ |
||||
p = ipr->p; |
||||
|
||||
#if IPV6_FRAG_COPYHEADER |
||||
if (IPV6_FRAG_REQROOM > 0) { |
||||
u8_t hdrerr; |
||||
/* Restore (only) the bytes that we overwrote beyond the fragment header.
|
||||
* Those bytes may belong to either the IPv6 header or an extension |
||||
* header placed before the fragment header. */ |
||||
MEMCPY(p->payload, ipr->orig_hdr, IPV6_FRAG_REQROOM); |
||||
/* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */ |
||||
hdrerr = pbuf_remove_header(p, IPV6_FRAG_REQROOM); |
||||
LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ |
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); |
||||
} |
||||
#endif |
||||
|
||||
/* We need to get rid of the fragment header itself, which is somewhere in
|
||||
* the middle of the packet (but still in the first pbuf of the chain). |
||||
* Getting rid of the header is required by RFC 2460 Sec. 4.5 and necessary |
||||
* in order to be able to reassemble packets that are close to full size |
||||
* (i.e., around 65535 bytes). We simply move up all the headers before the |
||||
* fragment header, including the IPv6 header, and adjust the payload start |
||||
* accordingly. This works because all these headers are in the first pbuf |
||||
* of the chain, and because the caller adjusts all its pointers on |
||||
* successful reassembly. */ |
||||
MEMMOVE((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr), ipr->iphdr, |
||||
(size_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr)); |
||||
|
||||
/* This is where the IPv6 header is now. */ |
||||
iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->iphdr + |
||||
sizeof(struct ip6_frag_hdr)); |
||||
|
||||
/* Adjust datagram length by adding header lengths. */ |
||||
ipr->datagram_len = (u16_t)(ipr->datagram_len + ((u8_t*)p->payload - (u8_t*)iphdr_ptr) |
||||
- IP6_HLEN); |
||||
|
||||
/* Set payload length in ip header. */ |
||||
iphdr_ptr->_plen = lwip_htons(ipr->datagram_len); |
||||
|
||||
/* With the fragment header gone, we now need to adjust the next-header
|
||||
* field of whatever header was originally before it. Since the packet made |
||||
* it through the original header processing routines at least up to the |
||||
* fragment header, we do not need any further sanity checks here. */ |
||||
if (IP6H_NEXTH(iphdr_ptr) == IP6_NEXTH_FRAGMENT) { |
||||
iphdr_ptr->_nexth = ipr->nexth; |
||||
} else { |
||||
u8_t *ptr = (u8_t *)iphdr_ptr + IP6_HLEN; |
||||
while (*ptr != IP6_NEXTH_FRAGMENT) { |
||||
ptr += 8 * (1 + ptr[1]); |
||||
} |
||||
*ptr = ipr->nexth; |
||||
} |
||||
|
||||
/* release the resources allocated 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. */ |
||||
clen = pbuf_clen(p); |
||||
LWIP_ASSERT("ip6_reass_pbufcount >= clen", ip6_reass_pbufcount >= clen); |
||||
ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - clen); |
||||
|
||||
/* Move pbuf back to IPv6 header. This should never fail. */ |
||||
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", 0); |
||||
pbuf_free(p); |
||||
return NULL; |
||||
} |
||||
|
||||
/* Return the pbuf chain */ |
||||
MIB2_STATS_INC(mib2.ip6reasmoks); |
||||
return p; |
||||
} |
||||
/* the datagram is not (yet?) reassembled completely */ |
||||
return NULL; |
||||
|
||||
nullreturn: |
||||
IP6_FRAG_STATS_INC(ip6_frag.drop); |
||||
pbuf_free(p); |
||||
return NULL; |
||||
} |
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ |
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_FRAG |
||||
|
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF |
||||
/** 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); |
||||
} |
||||
#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ |
||||
|
||||
/**
|
||||
* 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; |
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF |
||||
struct pbuf *newpbuf; |
||||
u16_t newpbuflen = 0; |
||||
u16_t left_to_copy; |
||||
#endif |
||||
static u32_t identification; |
||||
u16_t left, cop; |
||||
const u16_t mtu = nd6_get_destination_mtu(dest, netif); |
||||
const u16_t nfb = (u16_t)((mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK); |
||||
u16_t fragment_offset = 0; |
||||
u16_t last; |
||||
u16_t poff = IP6_HLEN; |
||||
|
||||
identification++; |
||||
|
||||
original_ip6hdr = (struct ip6_hdr *)p->payload; |
||||
|
||||
/* @todo we assume there are no options in the unfragmentable part (IPv6 header). */ |
||||
LWIP_ASSERT("p->tot_len >= IP6_HLEN", p->tot_len >= IP6_HLEN); |
||||
left = (u16_t)(p->tot_len - IP6_HLEN); |
||||
|
||||
while (left) { |
||||
last = (left <= nfb); |
||||
|
||||
/* Fill this fragment */ |
||||
cop = last ? left : nfb; |
||||
|
||||
#if LWIP_NETIF_TX_SINGLE_PBUF |
||||
rambuf = pbuf_alloc(PBUF_IP, cop + 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!", |
||||
(rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); |
||||
poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff); |
||||
/* make room for the IP header */ |
||||
if (pbuf_add_header(rambuf, IP6_HLEN)) { |
||||
pbuf_free(rambuf); |
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr); |
||||
return ERR_MEM; |
||||
} |
||||
/* fill in the IP header */ |
||||
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); |
||||
#else |
||||
/* 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!", |
||||
(rambuf->len >= (IP6_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 = (u16_t)(p->len - poff); |
||||
p->tot_len = (u16_t)(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 = (u16_t)(left_to_copy - newpbuflen); |
||||
if (left_to_copy) { |
||||
p = p->next; |
||||
} |
||||
} |
||||
poff = newpbuflen; |
||||
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ |
||||
|
||||
/* Set headers */ |
||||
frag_hdr->_nexth = original_ip6hdr->_nexth; |
||||
frag_hdr->reserved = 0; |
||||
frag_hdr->_fragment_offset = lwip_htons((u16_t)((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG))); |
||||
frag_hdr->_identification = lwip_htonl(identification); |
||||
|
||||
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); |
||||
IP6H_PLEN_SET(ip6hdr, (u16_t)(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 = (u16_t)(left - cop); |
||||
fragment_offset = (u16_t)(fragment_offset + cop); |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ |
||||
@ -1,626 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Multicast listener discovery |
||||
* |
||||
* @defgroup mld6 MLD6 |
||||
* @ingroup ip6 |
||||
* Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. |
||||
* No support for MLDv2.<br> |
||||
* Note: The allnodes (ff01::1, ff02::1) group is assumed be received by your |
||||
* netif since it must always be received for correct IPv6 operation (e.g. SLAAC). |
||||
* Ensure the netif filters are configured accordingly!<br> |
||||
* The netif flags also need NETIF_FLAG_MLD6 flag set to enable MLD6 on a |
||||
* netif ("netif->flags |= NETIF_FLAG_MLD6;").<br> |
||||
* To be called from TCPIP thread. |
||||
*/ |
||||
|
||||
/*
|
||||
* 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> |
||||
*/ |
||||
|
||||
/* 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/prot/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 |
||||
|
||||
/* Forward declarations. */ |
||||
static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr); |
||||
static err_t mld6_remove_group(struct netif *netif, struct mld_group *group); |
||||
static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); |
||||
static void mld6_send(struct netif *netif, 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 = netif_mld6_data(netif); |
||||
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL); |
||||
|
||||
while (group != NULL) { |
||||
struct mld_group *next = group->next; /* avoid use-after-free below */ |
||||
|
||||
/* disable the group at the MAC level */ |
||||
if (netif->mld_mac_filter != NULL) { |
||||
netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER); |
||||
} |
||||
|
||||
/* free group */ |
||||
memp_free(MEMP_MLD6_GROUP, 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 = netif_mld6_data(netif); |
||||
|
||||
while (group != NULL) { |
||||
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 = netif_mld6_data(ifp); |
||||
|
||||
while (group != NULL) { |
||||
if (ip6_addr_eq(&(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) { |
||||
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 = netif_mld6_data(ifp); |
||||
|
||||
netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group); |
||||
} |
||||
|
||||
return group; |
||||
} |
||||
|
||||
/**
|
||||
* Remove a group from the mld_group_list, but do not free it yet |
||||
* |
||||
* @param group the group to remove |
||||
* @return ERR_OK if group was removed from the list, an err_t otherwise |
||||
*/ |
||||
static err_t |
||||
mld6_remove_group(struct netif *netif, struct mld_group *group) |
||||
{ |
||||
err_t err = ERR_OK; |
||||
|
||||
/* Is it the first group? */ |
||||
if (netif_mld6_data(netif) == group) { |
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next); |
||||
} else { |
||||
/* look for group further down the list */ |
||||
struct mld_group *tmpGroup; |
||||
for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) { |
||||
if (tmpGroup->next == group) { |
||||
tmpGroup->next = group->next; |
||||
break; |
||||
} |
||||
} |
||||
/* Group not find group */ |
||||
if (tmpGroup == NULL) { |
||||
err = ERR_ARG; |
||||
} |
||||
} |
||||
|
||||
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 = netif_mld6_data(inp); |
||||
while (group != NULL) { |
||||
if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && |
||||
(!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { |
||||
mld6_delayed_report(group, lwip_ntohs(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, lwip_ntohs(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 one or all network interfaces. |
||||
* |
||||
* If the group is to be joined on all interfaces, the given group address must |
||||
* not have a zone set (i.e., it must have its zone index set to IP6_NO_ZONE). |
||||
* If the group is to be joined on one particular interface, the given group |
||||
* address may or may not have a zone set. |
||||
* |
||||
* @param srcaddr ipv6 address (zoned) of the network interface which should |
||||
* join a new group. If IP6_ADDR_ANY6, join on all netifs |
||||
* @param groupaddr the ipv6 address of the group to join (possibly but not |
||||
* necessarily zoned) |
||||
* @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; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* loop through netif's */ |
||||
NETIF_FOREACH(netif) { |
||||
/* 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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
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 (possibly but not |
||||
* necessarily zoned) |
||||
* @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; |
||||
#if LWIP_IPV6_SCOPES |
||||
ip6_addr_t ip6addr; |
||||
|
||||
/* If the address has a particular scope but no zone set, use the netif to
|
||||
* set one now. Within the mld6 module, all addresses are properly zoned. */ |
||||
if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) { |
||||
ip6_addr_set(&ip6addr, groupaddr); |
||||
ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif); |
||||
groupaddr = &ip6addr; |
||||
} |
||||
IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif); |
||||
#endif /* LWIP_IPV6_SCOPES */ |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* 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, NETIF_ADD_MAC_FILTER); |
||||
} |
||||
|
||||
/* Report our membership. */ |
||||
MLD6_STATS_INC(mld6.tx_report); |
||||
mld6_send(netif, 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. |
||||
* |
||||
* Zoning of address follows the same rules as @ref mld6_joingroup. |
||||
* |
||||
* @param srcaddr ipv6 address (zoned) of the network interface which should |
||||
* leave the group. If IP6_ADDR_ANY6, leave on all netifs |
||||
* @param groupaddr the ipv6 address of the group to leave (possibly, but not |
||||
* necessarily zoned) |
||||
* @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; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* loop through netif's */ |
||||
NETIF_FOREACH(netif) { |
||||
/* 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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
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 (possibly, but not |
||||
* necessarily zoned) |
||||
* @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; |
||||
#if LWIP_IPV6_SCOPES |
||||
ip6_addr_t ip6addr; |
||||
|
||||
if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) { |
||||
ip6_addr_set(&ip6addr, groupaddr); |
||||
ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif); |
||||
groupaddr = &ip6addr; |
||||
} |
||||
IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif); |
||||
#endif /* LWIP_IPV6_SCOPES */ |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* 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) { |
||||
/* Remove the group from the list */ |
||||
mld6_remove_group(netif, group); |
||||
|
||||
/* If we are the last reporter for this group */ |
||||
if (group->last_reporter_flag) { |
||||
MLD6_STATS_INC(mld6.tx_leave); |
||||
mld6_send(netif, group, ICMP6_TYPE_MLD); |
||||
} |
||||
|
||||
/* Disable the group at the MAC level */ |
||||
if (netif->mld_mac_filter != NULL) { |
||||
netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER); |
||||
} |
||||
|
||||
/* free group struct */ |
||||
memp_free(MEMP_MLD6_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 netif *netif; |
||||
|
||||
NETIF_FOREACH(netif) { |
||||
struct mld_group *group = netif_mld6_data(netif); |
||||
|
||||
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(netif, 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_in the max resp delay provided in the query |
||||
*/ |
||||
static void |
||||
mld6_delayed_report(struct mld_group *group, u16_t maxresp_in) |
||||
{ |
||||
/* Convert maxresp from milliseconds to tmr ticks */ |
||||
u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL; |
||||
if (maxresp == 0) { |
||||
maxresp = 1; |
||||
} |
||||
|
||||
#ifdef LWIP_RAND |
||||
/* Randomize maxresp. (if LWIP_RAND is supported) */ |
||||
maxresp = (u16_t)(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 netif *netif, 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) + MLD6_HBH_HLEN, PBUF_RAM); |
||||
if (p == NULL) { |
||||
MLD6_STATS_INC(mld6.memerr); |
||||
return; |
||||
} |
||||
|
||||
/* Move to make room for Hop-by-hop options header. */ |
||||
if (pbuf_remove_header(p, MLD6_HBH_HLEN)) { |
||||
pbuf_free(p); |
||||
MLD6_STATS_INC(mld6.lenerr); |
||||
return; |
||||
} |
||||
|
||||
/* Select our source address. */ |
||||
if (!ip6_addr_isvalid(netif_ip6_addr_state(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(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_copy_to_packed(mld_hdr->multicast_address, group->group_address); |
||||
|
||||
#if CHECKSUM_GEN_ICMP6 |
||||
IF__NETIF_CHECKSUM_ENABLED(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); |
||||
|
||||
if (type == ICMP6_TYPE_MLR) { |
||||
/* Remember we were the last to report */ |
||||
group->last_reporter_flag = 1; |
||||
} |
||||
|
||||
/* 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, netif); |
||||
pbuf_free(p); |
||||
} |
||||
|
||||
#endif /* LWIP_IPV6 */ |
||||
@ -1,447 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Dynamic pool memory manager |
||||
* |
||||
* lwIP has dedicated pools for many structures (netconn, protocol control blocks, |
||||
* packet buffers, ...). All these pools are managed here. |
||||
* |
||||
* @defgroup mempool Memory pools |
||||
* @ingroup infrastructure |
||||
* Custom memory pools |
||||
|
||||
*/ |
||||
|
||||
/*
|
||||
* 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> |
||||
* |
||||
*/ |
||||
|
||||
#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/altcp.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/priv/sockets_priv.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/priv/nd6_priv.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" |
||||
}; |
||||
|
||||
#ifdef LWIP_HOOK_FILENAME |
||||
#include LWIP_HOOK_FILENAME |
||||
#endif |
||||
|
||||
#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 or underflow |
||||
* (e.g. the restricted area after/before it has been altered) |
||||
* |
||||
* @param p the memp element to check |
||||
* @param desc the pool p comes from |
||||
*/ |
||||
static void |
||||
memp_overflow_check_element(struct memp *p, const struct memp_desc *desc) |
||||
{ |
||||
mem_overflow_check_raw((u8_t *)p + MEMP_SIZE, desc->size, "pool ", desc->desc); |
||||
} |
||||
|
||||
/**
|
||||
* Initialize the restricted area of on memp element. |
||||
*/ |
||||
static void |
||||
memp_overflow_init_element(struct memp *p, const struct memp_desc *desc) |
||||
{ |
||||
mem_overflow_init_raw((u8_t *)p + MEMP_SIZE, desc->size); |
||||
} |
||||
|
||||
#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 *)LWIP_MEM_ALIGN(memp_pools[i]->base); |
||||
for (j = 0; j < memp_pools[i]->num; ++j) { |
||||
memp_overflow_check_element(p, memp_pools[i]); |
||||
p = LWIP_ALIGNMENT_CAST(struct memp *, ((u8_t *)p + MEMP_SIZE + memp_pools[i]->size + MEM_SANITY_REGION_AFTER_ALIGNED)); |
||||
} |
||||
} |
||||
SYS_ARCH_UNPROTECT(old_level); |
||||
} |
||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */ |
||||
#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); |
||||
#if MEMP_MEM_INIT |
||||
/* force memset on pool memory */ |
||||
memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size |
||||
#if MEMP_OVERFLOW_CHECK |
||||
+ MEM_SANITY_REGION_AFTER_ALIGNED |
||||
#endif |
||||
)); |
||||
#endif |
||||
/* create a linked list of memp elements */ |
||||
for (i = 0; i < desc->num; ++i) { |
||||
memp->next = *desc->tab; |
||||
*desc->tab = memp; |
||||
#if MEMP_OVERFLOW_CHECK |
||||
memp_overflow_init_element(memp, desc); |
||||
#endif /* MEMP_OVERFLOW_CHECK */ |
||||
/* cast through void* to get rid of alignment warnings */ |
||||
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size |
||||
#if MEMP_OVERFLOW_CHECK |
||||
+ MEM_SANITY_REGION_AFTER_ALIGNED |
||||
#endif |
||||
); |
||||
} |
||||
#if MEMP_STATS |
||||
desc->stats->avail = desc->num; |
||||
#endif /* MEMP_STATS */ |
||||
#endif /* !MEMP_MEM_MALLOC */ |
||||
|
||||
#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) |
||||
desc->stats->name = desc->desc; |
||||
#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */ |
||||
} |
||||
|
||||
/**
|
||||
* 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; |
||||
#endif /* MEMP_MEM_MALLOC */ |
||||
|
||||
if (memp != NULL) { |
||||
#if !MEMP_MEM_MALLOC |
||||
#if MEMP_OVERFLOW_CHECK == 1 |
||||
memp_overflow_check_element(memp, desc); |
||||
#endif /* MEMP_OVERFLOW_CHECK */ |
||||
|
||||
*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 { |
||||
#if MEMP_STATS |
||||
desc->stats->err++; |
||||
#endif |
||||
SYS_ARCH_UNPROTECT(old_level); |
||||
LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc)); |
||||
} |
||||
|
||||
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(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 (mem == NULL) { |
||||
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 |
||||
} |
||||
@ -1,673 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Implementation of raw protocol PCBs for low-level handling of |
||||
* different types of protocols besides (or overriding) those |
||||
* already available in lwIP.<br> |
||||
* See also @ref raw_raw |
||||
* |
||||
* @defgroup raw_raw RAW |
||||
* @ingroup callbackstyle_api |
||||
* Implementation of raw protocol PCBs for low-level handling of |
||||
* different types of protocols besides (or overriding) those |
||||
* already available in lwIP.<br> |
||||
* @see @ref api |
||||
*/ |
||||
|
||||
/*
|
||||
* 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> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/def.h" |
||||
#include "lwip/memp.h" |
||||
#include "lwip/ip_addr.h" |
||||
#include "lwip/netif.h" |
||||
#include "lwip/raw.h" |
||||
#include "lwip/priv/raw_priv.h" |
||||
#include "lwip/stats.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_local_match(struct raw_pcb *pcb, u8_t broadcast) |
||||
{ |
||||
LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */ |
||||
|
||||
/* check if PCB is bound to specific netif */ |
||||
if ((pcb->netif_idx != NETIF_NO_INDEX) && |
||||
(pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) { |
||||
return 0; |
||||
} |
||||
|
||||
#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_eq(&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. |
||||
* |
||||
* Given an incoming IP datagram (as a chain of pbufs) this function |
||||
* finds a corresponding RAW PCB and calls the corresponding receive |
||||
* callback function. |
||||
* |
||||
* @param p pbuf to be demultiplexed to a RAW PCB. |
||||
* @param inp network interface on which the datagram was received. |
||||
* @return - 1 if the packet has been eaten by a RAW PCB receive |
||||
* callback function. The caller MAY NOT not reference the |
||||
* packet any longer, and MAY NOT call pbuf_free(). |
||||
* @return - 0 if packet is not eaten (pbuf is still referenced by the |
||||
* caller). |
||||
* |
||||
*/ |
||||
raw_input_state_t |
||||
raw_input(struct pbuf *p, struct netif *inp) |
||||
{ |
||||
struct raw_pcb *pcb, *prev; |
||||
s16_t proto; |
||||
raw_input_state_t ret = RAW_INPUT_NONE; |
||||
u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()); |
||||
|
||||
LWIP_UNUSED_ARG(inp); |
||||
|
||||
#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 (pcb != NULL) { |
||||
if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast) && |
||||
(((pcb->flags & RAW_FLAGS_CONNECTED) == 0) || |
||||
ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()))) { |
||||
/* receive callback function available? */ |
||||
if (pcb->recv != NULL) { |
||||
u8_t eaten; |
||||
#ifndef LWIP_NOASSERT |
||||
void *old_payload = p->payload; |
||||
#endif |
||||
ret = RAW_INPUT_DELIVERED; |
||||
/* the receive callback function did not eat the packet? */ |
||||
eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()); |
||||
if (eaten != 0) { |
||||
/* receive function ate the packet */ |
||||
p = NULL; |
||||
if (prev != NULL) { |
||||
/* move the pcb to the front of raw_pcbs so that is
|
||||
found faster next time */ |
||||
prev->next = pcb->next; |
||||
pcb->next = raw_pcbs; |
||||
raw_pcbs = pcb; |
||||
} |
||||
return RAW_INPUT_EATEN; |
||||
} 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; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Bind a RAW PCB. |
||||
* |
||||
* @param pcb RAW PCB to be bound with a local address ipaddr. |
||||
* @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to |
||||
* bind to all local interfaces. |
||||
* |
||||
* @return lwIP error code. |
||||
* - 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, const ip_addr_t *ipaddr) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
if ((pcb == NULL) || (ipaddr == NULL)) { |
||||
return ERR_VAL; |
||||
} |
||||
ip_addr_set_ipaddr(&pcb->local_ip, ipaddr); |
||||
#if LWIP_IPV6 && LWIP_IPV6_SCOPES |
||||
/* If the given IP address should have a zone but doesn't, assign one now.
|
||||
* This is legacy support: scope-aware callers should always provide properly |
||||
* zoned source addresses. */ |
||||
if (IP_IS_V6(&pcb->local_ip) && |
||||
ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNKNOWN)) { |
||||
ip6_addr_select_zone(ip_2_ip6(&pcb->local_ip), ip_2_ip6(&pcb->local_ip)); |
||||
} |
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ |
||||
return ERR_OK; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Bind an RAW PCB to a specific netif. |
||||
* After calling this function, all packets received via this PCB |
||||
* are guaranteed to have come in via the specified netif, and all |
||||
* outgoing packets will go out via the specified netif. |
||||
* |
||||
* @param pcb RAW PCB to be bound with netif. |
||||
* @param netif netif to bind to. Can be NULL. |
||||
* |
||||
* @see raw_disconnect() |
||||
*/ |
||||
void |
||||
raw_bind_netif(struct raw_pcb *pcb, const struct netif *netif) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
if (netif != NULL) { |
||||
pcb->netif_idx = netif_get_index(netif); |
||||
} else { |
||||
pcb->netif_idx = NETIF_NO_INDEX; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @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 |
||||
* |
||||
* This will associate the RAW PCB with the remote address. |
||||
* |
||||
* @param pcb RAW PCB to be connected with remote address ipaddr and port. |
||||
* @param ipaddr remote IP address to connect with. |
||||
* |
||||
* @return lwIP error code |
||||
* |
||||
* @see raw_disconnect() and raw_sendto() |
||||
*/ |
||||
err_t |
||||
raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
if ((pcb == NULL) || (ipaddr == NULL)) { |
||||
return ERR_VAL; |
||||
} |
||||
ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr); |
||||
#if LWIP_IPV6 && LWIP_IPV6_SCOPES |
||||
/* If the given IP address should have a zone but doesn't, assign one now,
|
||||
* using the bound address to make a more informed decision when possible. */ |
||||
if (IP_IS_V6(&pcb->remote_ip) && |
||||
ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) { |
||||
ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip)); |
||||
} |
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ |
||||
raw_set_flags(pcb, RAW_FLAGS_CONNECTED); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Disconnect a RAW PCB. |
||||
* |
||||
* @param pcb the raw pcb to disconnect. |
||||
*/ |
||||
void |
||||
raw_disconnect(struct raw_pcb *pcb) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
/* reset remote address association */ |
||||
#if LWIP_IPV4 && LWIP_IPV6 |
||||
if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { |
||||
ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE); |
||||
} else { |
||||
#endif |
||||
ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip); |
||||
#if LWIP_IPV4 && LWIP_IPV6 |
||||
} |
||||
#endif |
||||
pcb->netif_idx = NETIF_NO_INDEX; |
||||
/* mark PCB as unconnected */ |
||||
raw_clear_flags(pcb, RAW_FLAGS_CONNECTED); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Set the callback function for received packets that match the |
||||
* raw PCB's protocol and binding. |
||||
* |
||||
* The callback function MUST either |
||||
* - eat the packet by calling pbuf_free() and returning non-zero. The |
||||
* packet will not be passed to other raw PCBs or other protocol layers. |
||||
* - not free the packet, and return zero. The packet will be matched |
||||
* against further PCBs and/or forwarded to another protocol layers. |
||||
*/ |
||||
void |
||||
raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
/* remember recv() callback and user data */ |
||||
pcb->recv = recv; |
||||
pcb->recv_arg = recv_arg; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Send the raw IP packet to the given address. An IP header will be prepended |
||||
* to the packet, unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that |
||||
* case, the packet must include an IP header, which will then be sent as is. |
||||
* |
||||
* @param pcb the raw pcb which to send |
||||
* @param p the IP payload to send |
||||
* @param ipaddr the destination address of the IP packet |
||||
* |
||||
*/ |
||||
err_t |
||||
raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) |
||||
{ |
||||
struct netif *netif; |
||||
const ip_addr_t *src_ip; |
||||
|
||||
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")); |
||||
|
||||
if (pcb->netif_idx != NETIF_NO_INDEX) { |
||||
netif = netif_get_by_index(pcb->netif_idx); |
||||
} else { |
||||
#if LWIP_MULTICAST_TX_OPTIONS |
||||
netif = NULL; |
||||
if (ip_addr_ismulticast(ipaddr)) { |
||||
/* For multicast-destined packets, use the user-provided interface index to
|
||||
* determine the outgoing interface, if an interface index is set and a |
||||
* matching netif can be found. Otherwise, fall back to regular routing. */ |
||||
netif = netif_get_by_index(pcb->mcast_ifindex); |
||||
} |
||||
|
||||
if (netif == NULL) |
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */ |
||||
{ |
||||
netif = ip_route(&pcb->local_ip, ipaddr); |
||||
} |
||||
} |
||||
|
||||
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, ipaddr); |
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("\n")); |
||||
return ERR_RTE; |
||||
} |
||||
|
||||
if (ip_addr_isany(&pcb->local_ip) || ip_addr_ismulticast(&pcb->local_ip)) { |
||||
/* use outgoing network interface IP address as source address */ |
||||
src_ip = ip_netif_get_local_ip(netif, ipaddr); |
||||
#if LWIP_IPV6 |
||||
if (src_ip == NULL) { |
||||
return ERR_RTE; |
||||
} |
||||
#endif /* LWIP_IPV6 */ |
||||
} else { |
||||
/* use RAW PCB local IP address as source address */ |
||||
src_ip = &pcb->local_ip; |
||||
} |
||||
|
||||
return raw_sendto_if_src(pcb, p, ipaddr, netif, src_ip); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Send the raw IP packet to the given address, using a particular outgoing |
||||
* netif and source IP address. An IP header will be prepended to the packet, |
||||
* unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that case, the |
||||
* packet must include an IP header, which will then be sent as is. |
||||
* |
||||
* @param pcb RAW PCB used to send the data |
||||
* @param p chain of pbufs to be sent |
||||
* @param dst_ip destination IP address |
||||
* @param netif the netif used for sending |
||||
* @param src_ip source IP address |
||||
*/ |
||||
err_t |
||||
raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, |
||||
struct netif *netif, const ip_addr_t *src_ip) |
||||
{ |
||||
err_t err; |
||||
struct pbuf *q; /* q will be sent down the stack */ |
||||
u16_t header_size; |
||||
u8_t ttl; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
if ((pcb == NULL) || (dst_ip == NULL) || (netif == NULL) || (src_ip == NULL) || |
||||
!IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) { |
||||
return ERR_VAL; |
||||
} |
||||
|
||||
header_size = ( |
||||
#if LWIP_IPV4 && LWIP_IPV6 |
||||
IP_IS_V6(dst_ip) ? IP6_HLEN : IP_HLEN); |
||||
#elif LWIP_IPV4 |
||||
IP_HLEN); |
||||
#else |
||||
IP6_HLEN); |
||||
#endif |
||||
|
||||
/* Handle the HDRINCL option as an exception: none of the code below applies
|
||||
* to this case, and sending the packet needs to be done differently too. */ |
||||
if (pcb->flags & RAW_FLAGS_HDRINCL) { |
||||
/* A full header *must* be present in the first pbuf of the chain, as the
|
||||
* output routines may access its fields directly. */ |
||||
if (p->len < header_size) { |
||||
return ERR_VAL; |
||||
} |
||||
/* @todo multicast loop support, if at all desired for this scenario.. */ |
||||
NETIF_SET_HINTS(netif, &pcb->netif_hints); |
||||
err = ip_output_if_hdrincl(p, src_ip, dst_ip, netif); |
||||
NETIF_RESET_HINTS(netif); |
||||
return err; |
||||
} |
||||
|
||||
/* packet too large to add an IP header without causing an overflow? */ |
||||
if ((u16_t)(p->tot_len + header_size) < p->tot_len) { |
||||
return ERR_MEM; |
||||
} |
||||
/* not enough space to add an IP header to first pbuf in given p chain? */ |
||||
if (pbuf_add_header(p, header_size)) { |
||||
/* allocate header in new pbuf */ |
||||
q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); |
||||
/* new header pbuf could not be allocated? */ |
||||
if (q == NULL) { |
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); |
||||
return ERR_MEM; |
||||
} |
||||
if (p->tot_len != 0) { |
||||
/* chain header q in front of given pbuf p */ |
||||
pbuf_chain(q, p); |
||||
} |
||||
/* { first pbuf q points to header pbuf } */ |
||||
LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); |
||||
} else { |
||||
/* first pbuf q equals given pbuf */ |
||||
q = p; |
||||
if (pbuf_remove_header(q, header_size)) { |
||||
LWIP_ASSERT("Can't restore header we just removed!", 0); |
||||
return ERR_MEM; |
||||
} |
||||
} |
||||
|
||||
#if IP_SOF_BROADCAST |
||||
if (IP_IS_V4(dst_ip)) { |
||||
/* broadcast filter? */ |
||||
if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(dst_ip, netif)) { |
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); |
||||
/* free any temporary header pbuf allocated by pbuf_header() */ |
||||
if (q != p) { |
||||
pbuf_free(q); |
||||
} |
||||
return ERR_VAL; |
||||
} |
||||
} |
||||
#endif /* IP_SOF_BROADCAST */ |
||||
|
||||
/* Multicast Loop? */ |
||||
#if LWIP_MULTICAST_TX_OPTIONS |
||||
if (((pcb->flags & RAW_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) { |
||||
q->flags |= PBUF_FLAG_MCASTLOOP; |
||||
} |
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */ |
||||
|
||||
#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 |
||||
|
||||
/* Determine TTL to use */ |
||||
#if LWIP_MULTICAST_TX_OPTIONS |
||||
ttl = (ip_addr_ismulticast(dst_ip) ? raw_get_multicast_ttl(pcb) : pcb->ttl); |
||||
#else /* LWIP_MULTICAST_TX_OPTIONS */ |
||||
ttl = pcb->ttl; |
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */ |
||||
|
||||
NETIF_SET_HINTS(netif, &pcb->netif_hints); |
||||
err = ip_output_if(q, src_ip, dst_ip, ttl, pcb->tos, pcb->protocol, netif); |
||||
NETIF_RESET_HINTS(netif); |
||||
|
||||
/* did we chain a header earlier? */ |
||||
if (q != p) { |
||||
/* free the header */ |
||||
pbuf_free(q); |
||||
} |
||||
return err; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Send the raw IP packet to the address given by raw_connect() |
||||
* |
||||
* @param pcb the raw pcb which to send |
||||
* @param p the IP payload to send |
||||
* |
||||
*/ |
||||
err_t |
||||
raw_send(struct raw_pcb *pcb, struct pbuf *p) |
||||
{ |
||||
return raw_sendto(pcb, p, &pcb->remote_ip); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Remove an RAW PCB. |
||||
* |
||||
* @param pcb RAW PCB to be removed. The PCB is removed from the list of |
||||
* RAW PCB's and the data structure is freed from memory. |
||||
* |
||||
* @see raw_new() |
||||
*/ |
||||
void |
||||
raw_remove(struct raw_pcb *pcb) |
||||
{ |
||||
struct raw_pcb *pcb2; |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
/* pcb to be removed is first in list? */ |
||||
if (raw_pcbs == pcb) { |
||||
/* make list start at 2nd pcb */ |
||||
raw_pcbs = raw_pcbs->next; |
||||
/* pcb not 1st in list */ |
||||
} else { |
||||
for (pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { |
||||
/* find pcb in raw_pcbs list */ |
||||
if (pcb2->next != NULL && pcb2->next == pcb) { |
||||
/* remove pcb from list */ |
||||
pcb2->next = pcb->next; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
memp_free(MEMP_RAW_PCB, pcb); |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup raw_raw |
||||
* Create a RAW PCB. |
||||
* |
||||
* @return The RAW PCB which was created. NULL if the PCB data structure |
||||
* could not be allocated. |
||||
* |
||||
* @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) |
||||
* |
||||
* @see raw_remove() |
||||
*/ |
||||
struct raw_pcb * |
||||
raw_new(u8_t proto) |
||||
{ |
||||
struct raw_pcb *pcb; |
||||
|
||||
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB); |
||||
/* could allocate RAW PCB? */ |
||||
if (pcb != NULL) { |
||||
/* initialize PCB to all zeroes */ |
||||
memset(pcb, 0, sizeof(struct raw_pcb)); |
||||
pcb->protocol = proto; |
||||
pcb->ttl = RAW_TTL; |
||||
#if LWIP_MULTICAST_TX_OPTIONS |
||||
raw_set_multicast_ttl(pcb, RAW_TTL); |
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */ |
||||
pcb_tci_init(pcb); |
||||
pcb->next = raw_pcbs; |
||||
raw_pcbs = pcb; |
||||
} |
||||
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 @ref lwip_ip_addr_type definitions. |
||||
* If you want to listen to IPv4 and IPv6 (dual-stack) packets, |
||||
* supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE. |
||||
* @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; |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
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; |
||||
} |
||||
|
||||
/** This function is called from netif.c when address is changed
|
||||
* |
||||
* @param old_addr IP address of the netif before change |
||||
* @param new_addr IP address of the netif after change |
||||
*/ |
||||
void raw_netif_ip_addr_changed(const ip_addr_t *old_addr, const ip_addr_t *new_addr) |
||||
{ |
||||
struct raw_pcb *rpcb; |
||||
|
||||
if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) { |
||||
for (rpcb = raw_pcbs; rpcb != NULL; rpcb = rpcb->next) { |
||||
/* PCB bound to current local interface address? */ |
||||
if (ip_addr_eq(&rpcb->local_ip, old_addr)) { |
||||
/* The PCB is bound to the old ipaddr and
|
||||
* is set to bound to the new one instead */ |
||||
ip_addr_copy(rpcb->local_ip, *new_addr); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif /* LWIP_RAW */ |
||||
@ -1,168 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Statistics module |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* 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> |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ |
||||
|
||||
#include "lwip/def.h" |
||||
#include "lwip/stats.h" |
||||
#include "lwip/mem.h" |
||||
#include "lwip/debug.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
struct stats_ lwip_stats; |
||||
|
||||
void |
||||
stats_init(void) |
||||
{ |
||||
#ifdef LWIP_DEBUG |
||||
#if MEM_STATS |
||||
lwip_stats.mem.name = "MEM"; |
||||
#endif /* MEM_STATS */ |
||||
#endif /* LWIP_DEBUG */ |
||||
} |
||||
|
||||
#if LWIP_STATS_DISPLAY |
||||
void |
||||
stats_display_proto(struct stats_proto *proto, const char *name) |
||||
{ |
||||
LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); |
||||
LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); |
||||
LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); |
||||
LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); |
||||
LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); |
||||
LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); |
||||
LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); |
||||
LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); |
||||
LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); |
||||
LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); |
||||
LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); |
||||
LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); |
||||
LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); |
||||
} |
||||
|
||||
#if IGMP_STATS || MLD6_STATS |
||||
void |
||||
stats_display_igmp(struct stats_igmp *igmp, const char *name) |
||||
{ |
||||
LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); |
||||
LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); |
||||
LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); |
||||
LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); |
||||
LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); |
||||
LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); |
||||
LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); |
||||
LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); |
||||
LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); |
||||
LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group)); |
||||
LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general)); |
||||
LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); |
||||
LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); |
||||
LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); |
||||
LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n", igmp->tx_report)); |
||||
} |
||||
#endif /* IGMP_STATS || MLD6_STATS */ |
||||
|
||||
#if MEM_STATS || MEMP_STATS |
||||
void |
||||
stats_display_mem(struct stats_mem *mem, const char *name) |
||||
{ |
||||
LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); |
||||
LWIP_PLATFORM_DIAG(("avail: %"MEM_SIZE_F"\n\t", mem->avail)); |
||||
LWIP_PLATFORM_DIAG(("used: %"MEM_SIZE_F"\n\t", mem->used)); |
||||
LWIP_PLATFORM_DIAG(("max: %"MEM_SIZE_F"\n\t", mem->max)); |
||||
LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n", mem->err)); |
||||
} |
||||
|
||||
#if MEMP_STATS |
||||
void |
||||
stats_display_memp(struct stats_mem *mem, int idx) |
||||
{ |
||||
if (idx < MEMP_MAX) { |
||||
stats_display_mem(mem, mem->name); |
||||
} |
||||
} |
||||
#endif /* MEMP_STATS */ |
||||
#endif /* MEM_STATS || MEMP_STATS */ |
||||
|
||||
#if SYS_STATS |
||||
void |
||||
stats_display_sys(struct stats_sys *sys) |
||||
{ |
||||
LWIP_PLATFORM_DIAG(("\nSYS\n\t")); |
||||
LWIP_PLATFORM_DIAG(("sem.used: %"STAT_COUNTER_F"\n\t", sys->sem.used)); |
||||
LWIP_PLATFORM_DIAG(("sem.max: %"STAT_COUNTER_F"\n\t", sys->sem.max)); |
||||
LWIP_PLATFORM_DIAG(("sem.err: %"STAT_COUNTER_F"\n\t", sys->sem.err)); |
||||
LWIP_PLATFORM_DIAG(("mutex.used: %"STAT_COUNTER_F"\n\t", sys->mutex.used)); |
||||
LWIP_PLATFORM_DIAG(("mutex.max: %"STAT_COUNTER_F"\n\t", sys->mutex.max)); |
||||
LWIP_PLATFORM_DIAG(("mutex.err: %"STAT_COUNTER_F"\n\t", sys->mutex.err)); |
||||
LWIP_PLATFORM_DIAG(("mbox.used: %"STAT_COUNTER_F"\n\t", sys->mbox.used)); |
||||
LWIP_PLATFORM_DIAG(("mbox.max: %"STAT_COUNTER_F"\n\t", sys->mbox.max)); |
||||
LWIP_PLATFORM_DIAG(("mbox.err: %"STAT_COUNTER_F"\n", sys->mbox.err)); |
||||
} |
||||
#endif /* SYS_STATS */ |
||||
|
||||
void |
||||
stats_display(void) |
||||
{ |
||||
s16_t i; |
||||
|
||||
LINK_STATS_DISPLAY(); |
||||
ETHARP_STATS_DISPLAY(); |
||||
IPFRAG_STATS_DISPLAY(); |
||||
IP6_FRAG_STATS_DISPLAY(); |
||||
IP_STATS_DISPLAY(); |
||||
ND6_STATS_DISPLAY(); |
||||
IP6_STATS_DISPLAY(); |
||||
IGMP_STATS_DISPLAY(); |
||||
MLD6_STATS_DISPLAY(); |
||||
ICMP_STATS_DISPLAY(); |
||||
ICMP6_STATS_DISPLAY(); |
||||
UDP_STATS_DISPLAY(); |
||||
TCP_STATS_DISPLAY(); |
||||
MEM_STATS_DISPLAY(); |
||||
for (i = 0; i < MEMP_MAX; i++) { |
||||
MEMP_STATS_DISPLAY(i); |
||||
} |
||||
SYS_STATS_DISPLAY(); |
||||
} |
||||
#endif /* LWIP_STATS_DISPLAY */ |
||||
|
||||
#endif /* LWIP_STATS */ |
||||
@ -1,148 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* lwIP Operating System abstraction |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* 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 sys_layer Porting (system abstraction layer) |
||||
* @ingroup lwip |
||||
* |
||||
* @defgroup sys_os OS abstraction layer |
||||
* @ingroup sys_layer |
||||
* No need to implement functions in this section in NO_SYS mode. |
||||
* The OS-specific code should be implemented in arch/sys_arch.h |
||||
* and sys_arch.c of your port. |
||||
* |
||||
* The operating system emulation layer provides a common interface |
||||
* between the lwIP code and the underlying operating system kernel. The |
||||
* general idea is that porting lwIP to new architectures requires only |
||||
* small changes to a few header files and a new sys_arch |
||||
* implementation. It is also possible to do a sys_arch implementation |
||||
* that does not rely on any underlying operating system. |
||||
* |
||||
* The sys_arch provides semaphores, mailboxes and mutexes to lwIP. For the full |
||||
* lwIP functionality, multiple threads support can be implemented in the |
||||
* sys_arch, but this is not required for the basic lwIP |
||||
* functionality. Timer scheduling is implemented in lwIP, but can be implemented |
||||
* by the sys_arch port (LWIP_TIMERS_CUSTOM==1). |
||||
* |
||||
* In addition to the source file providing the functionality of sys_arch, |
||||
* the OS emulation layer must provide several header files defining |
||||
* macros used throughout lwip. The files required and the macros they |
||||
* must define are listed below the sys_arch description. |
||||
* |
||||
* Since lwIP 1.4.0, semaphore, mutexes and mailbox functions are prototyped in a way that |
||||
* allows both using pointers or actual OS structures to be used. This way, memory |
||||
* required for such types can be either allocated in place (globally or on the |
||||
* stack) or on the heap (allocated internally in the "*_new()" functions). |
||||
* |
||||
* Note: |
||||
* ----- |
||||
* Be careful with using mem_malloc() in sys_arch. When malloc() refers to |
||||
* mem_malloc() you can run into a circular function call problem. In mem.c |
||||
* mem_init() tries to allocate a semaphore using mem_malloc, which of course |
||||
* can't be performed when sys_arch uses mem_malloc. |
||||
* |
||||
* @defgroup sys_sem Semaphores |
||||
* @ingroup sys_os |
||||
* Semaphores can be either counting or binary - lwIP works with both |
||||
* kinds. |
||||
* Semaphores are represented by the type "sys_sem_t" which is typedef'd |
||||
* in the sys_arch.h file. Mailboxes are equivalently represented by the |
||||
* type "sys_mbox_t". Mutexes are represented by the type "sys_mutex_t". |
||||
* lwIP does not place any restrictions on how these types are represented |
||||
* internally. |
||||
* |
||||
* @defgroup sys_mutex Mutexes |
||||
* @ingroup sys_os |
||||
* Mutexes are recommended to correctly handle priority inversion, |
||||
* especially if you use LWIP_CORE_LOCKING . |
||||
* |
||||
* @defgroup sys_mbox Mailboxes |
||||
* @ingroup sys_os |
||||
* Mailboxes should be implemented as a queue which allows multiple messages |
||||
* to be posted (implementing as a rendez-vous point where only one message can be |
||||
* posted at a time can have a highly negative impact on performance). A message |
||||
* in a mailbox is just a pointer, nothing more. |
||||
* |
||||
* @defgroup sys_time Time |
||||
* @ingroup sys_layer |
||||
* |
||||
* @defgroup sys_prot Critical sections |
||||
* @ingroup sys_layer |
||||
* Used to protect short regions of code against concurrent access. |
||||
* - Your system is a bare-metal system (probably with an RTOS) |
||||
* and interrupts are under your control: |
||||
* Implement this as LockInterrupts() / UnlockInterrupts() |
||||
* - Your system uses an RTOS with deferred interrupt handling from a |
||||
* worker thread: Implement as a global mutex or lock/unlock scheduler |
||||
* - Your system uses a high-level OS with e.g. POSIX signals: |
||||
* Implement as a global mutex |
||||
* |
||||
* @defgroup sys_misc Misc |
||||
* @ingroup sys_os |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#include "lwip/sys.h" |
||||
|
||||
/* Most of the functions defined in sys.h must be implemented in the
|
||||
* architecture-dependent file sys_arch.c */ |
||||
|
||||
#if !NO_SYS |
||||
|
||||
#ifndef sys_msleep |
||||
/**
|
||||
* Sleep for some ms. Timeouts are NOT processed while sleeping. |
||||
* |
||||
* @param ms number of milliseconds to sleep |
||||
*/ |
||||
void |
||||
sys_msleep(u32_t ms) |
||||
{ |
||||
if (ms > 0) { |
||||
sys_sem_t delaysem; |
||||
err_t err = sys_sem_new(&delaysem, 0); |
||||
if (err == ERR_OK) { |
||||
sys_arch_sem_wait(&delaysem, ms); |
||||
sys_sem_free(&delaysem); |
||||
} |
||||
} |
||||
} |
||||
#endif /* sys_msleep */ |
||||
|
||||
#endif /* !NO_SYS */ |
||||
@ -1,451 +0,0 @@
|
||||
/**
|
||||
* @file |
||||
* Stack-internal timers implementation. |
||||
* This file includes timer callbacks for stack-internal timers as well as |
||||
* functions to set up or stop timers and check for expired timers. |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* 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> |
||||
* Simon Goldschmidt |
||||
* |
||||
*/ |
||||
|
||||
#include "lwip/opt.h" |
||||
|
||||
#include "lwip/timeouts.h" |
||||
#include "lwip/priv/tcp_priv.h" |
||||
|
||||
#include "lwip/def.h" |
||||
#include "lwip/memp.h" |
||||
#include "lwip/priv/tcpip_priv.h" |
||||
|
||||
#include "lwip/ip4_frag.h" |
||||
#include "lwip/etharp.h" |
||||
#include "lwip/dhcp.h" |
||||
#include "lwip/acd.h" |
||||
#include "lwip/igmp.h" |
||||
#include "lwip/dns.h" |
||||
#include "lwip/nd6.h" |
||||
#include "lwip/ip6_frag.h" |
||||
#include "lwip/mld6.h" |
||||
#include "lwip/dhcp6.h" |
||||
#include "lwip/sys.h" |
||||
#include "lwip/pbuf.h" |
||||
|
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
#define HANDLER(x) x, #x |
||||
#else /* LWIP_DEBUG_TIMERNAMES */ |
||||
#define HANDLER(x) x |
||||
#endif /* LWIP_DEBUG_TIMERNAMES */ |
||||
|
||||
#define LWIP_MAX_TIMEOUT 0x7fffffff |
||||
|
||||
/* Check if timer's expiry time is greater than time and care about u32_t wraparounds */ |
||||
#define TIME_LESS_THAN(t, compare_to) ( (((u32_t)((t)-(compare_to))) > LWIP_MAX_TIMEOUT) ? 1 : 0 ) |
||||
|
||||
/** This array contains all stack-internal cyclic timers. To get the number of
|
||||
* timers, use LWIP_ARRAYSIZE() */ |
||||
const struct lwip_cyclic_timer lwip_cyclic_timers[] = { |
||||
#if LWIP_TCP |
||||
/* The TCP timer is a special case: it does not have to run always and
|
||||
is triggered to start from TCP using tcp_timer_needed() */ |
||||
{TCP_TMR_INTERVAL, HANDLER(tcp_tmr)}, |
||||
#endif /* LWIP_TCP */ |
||||
#if LWIP_IPV4 |
||||
#if IP_REASSEMBLY |
||||
{IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)}, |
||||
#endif /* IP_REASSEMBLY */ |
||||
#if LWIP_ARP |
||||
{ARP_TMR_INTERVAL, HANDLER(etharp_tmr)}, |
||||
#endif /* LWIP_ARP */ |
||||
#if LWIP_DHCP |
||||
{DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)}, |
||||
{DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)}, |
||||
#endif /* LWIP_DHCP */ |
||||
#if LWIP_ACD |
||||
{ACD_TMR_INTERVAL, HANDLER(acd_tmr)}, |
||||
#endif /* LWIP_ACD */ |
||||
#if LWIP_IGMP |
||||
{IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)}, |
||||
#endif /* LWIP_IGMP */ |
||||
#endif /* LWIP_IPV4 */ |
||||
#if LWIP_DNS |
||||
{DNS_TMR_INTERVAL, HANDLER(dns_tmr)}, |
||||
#endif /* LWIP_DNS */ |
||||
#if LWIP_IPV6 |
||||
{ND6_TMR_INTERVAL, HANDLER(nd6_tmr)}, |
||||
#if LWIP_IPV6_REASS |
||||
{IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)}, |
||||
#endif /* LWIP_IPV6_REASS */ |
||||
#if LWIP_IPV6_MLD |
||||
{MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)}, |
||||
#endif /* LWIP_IPV6_MLD */ |
||||
#if LWIP_IPV6_DHCP6 |
||||
{DHCP6_TIMER_MSECS, HANDLER(dhcp6_tmr)}, |
||||
#endif /* LWIP_IPV6_DHCP6 */ |
||||
#endif /* LWIP_IPV6 */ |
||||
}; |
||||
const int lwip_num_cyclic_timers = LWIP_ARRAYSIZE(lwip_cyclic_timers); |
||||
|
||||
#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM |
||||
|
||||
/** The one and only timeout list */ |
||||
static struct sys_timeo *next_timeout; |
||||
|
||||
static u32_t current_timeout_due_time; |
||||
|
||||
#if LWIP_TESTMODE |
||||
struct sys_timeo** |
||||
sys_timeouts_get_next_timeout(void) |
||||
{ |
||||
return &next_timeout; |
||||
} |
||||
#endif |
||||
|
||||
#if LWIP_TCP |
||||
/** global variable that shows if the tcp timer is currently scheduled or not */ |
||||
static int tcpip_tcp_timer_active; |
||||
|
||||
/**
|
||||
* Timer callback function that calls tcp_tmr() and reschedules itself. |
||||
* |
||||
* @param arg unused argument |
||||
*/ |
||||
static void |
||||
tcpip_tcp_timer(void *arg) |
||||
{ |
||||
LWIP_UNUSED_ARG(arg); |
||||
|
||||
/* call TCP timer handler */ |
||||
tcp_tmr(); |
||||
/* timer still needed? */ |
||||
if (tcp_active_pcbs || tcp_tw_pcbs) { |
||||
/* restart timer */ |
||||
sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); |
||||
} else { |
||||
/* disable timer */ |
||||
tcpip_tcp_timer_active = 0; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Called from TCP_REG when registering a new PCB: |
||||
* the reason is to have the TCP timer only running when |
||||
* there are active (or time-wait) PCBs. |
||||
*/ |
||||
void |
||||
tcp_timer_needed(void) |
||||
{ |
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* timer is off but needed again? */ |
||||
if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { |
||||
/* enable and start timer */ |
||||
tcpip_tcp_timer_active = 1; |
||||
sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); |
||||
} |
||||
} |
||||
#endif /* LWIP_TCP */ |
||||
|
||||
static void |
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg, const char *handler_name) |
||||
#else /* LWIP_DEBUG_TIMERNAMES */ |
||||
sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg) |
||||
#endif |
||||
{ |
||||
struct sys_timeo *timeout, *t; |
||||
|
||||
timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); |
||||
if (timeout == NULL) { |
||||
LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); |
||||
return; |
||||
} |
||||
|
||||
timeout->next = NULL; |
||||
timeout->h = handler; |
||||
timeout->arg = arg; |
||||
timeout->time = abs_time; |
||||
|
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
timeout->handler_name = handler_name; |
||||
LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p abs_time=%"U32_F" handler=%s arg=%p\n", |
||||
(void *)timeout, abs_time, handler_name, (void *)arg)); |
||||
#endif /* LWIP_DEBUG_TIMERNAMES */ |
||||
|
||||
if (next_timeout == NULL) { |
||||
next_timeout = timeout; |
||||
return; |
||||
} |
||||
if (TIME_LESS_THAN(timeout->time, next_timeout->time)) { |
||||
timeout->next = next_timeout; |
||||
next_timeout = timeout; |
||||
} else { |
||||
for (t = next_timeout; t != NULL; t = t->next) { |
||||
if ((t->next == NULL) || TIME_LESS_THAN(timeout->time, t->next->time)) { |
||||
timeout->next = t->next; |
||||
t->next = timeout; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Timer callback function that calls cyclic->handler() and reschedules itself. |
||||
* |
||||
* @param arg unused argument |
||||
*/ |
||||
#if !LWIP_TESTMODE |
||||
static |
||||
#endif |
||||
void |
||||
lwip_cyclic_timer(void *arg) |
||||
{ |
||||
u32_t now; |
||||
u32_t next_timeout_time; |
||||
const struct lwip_cyclic_timer *cyclic = (const struct lwip_cyclic_timer *)arg; |
||||
|
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name)); |
||||
#endif |
||||
cyclic->handler(); |
||||
|
||||
now = sys_now(); |
||||
next_timeout_time = (u32_t)(current_timeout_due_time + cyclic->interval_ms); /* overflow handled by TIME_LESS_THAN macro */ |
||||
if (TIME_LESS_THAN(next_timeout_time, now)) { |
||||
/* timer would immediately expire again -> "overload" -> restart without any correction */ |
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg, cyclic->handler_name); |
||||
#else |
||||
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg); |
||||
#endif |
||||
|
||||
} else { |
||||
/* correct cyclic interval with handler execution delay and sys_check_timeouts jitter */ |
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg, cyclic->handler_name); |
||||
#else |
||||
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg); |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
/** Initialize this module */ |
||||
void sys_timeouts_init(void) |
||||
{ |
||||
size_t i; |
||||
/* tcp_tmr() at index 0 is started on demand */ |
||||
for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) { |
||||
/* we have to cast via size_t to get rid of const warning
|
||||
(this is OK as cyclic_timer() casts back to const* */ |
||||
sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i])); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Create a one-shot timer (aka timeout). Timeouts are processed in the |
||||
* following cases: |
||||
* - while waiting for a message using sys_timeouts_mbox_fetch() |
||||
* - by calling sys_check_timeouts() (NO_SYS==1 only) |
||||
* |
||||
* @param msecs time in milliseconds after that the timer should expire |
||||
* @param handler callback function to call when msecs have elapsed |
||||
* @param arg argument to pass to the callback function |
||||
*/ |
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
void |
||||
sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char *handler_name) |
||||
#else /* LWIP_DEBUG_TIMERNAMES */ |
||||
void |
||||
sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) |
||||
#endif /* LWIP_DEBUG_TIMERNAMES */ |
||||
{ |
||||
u32_t next_timeout_time; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
LWIP_ASSERT("Timeout time too long, max is LWIP_UINT32_MAX/4 msecs", msecs <= (LWIP_UINT32_MAX / 4)); |
||||
|
||||
next_timeout_time = (u32_t)(sys_now() + msecs); /* overflow handled by TIME_LESS_THAN macro */ |
||||
|
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
sys_timeout_abs(next_timeout_time, handler, arg, handler_name); |
||||
#else |
||||
sys_timeout_abs(next_timeout_time, handler, arg); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
* Go through timeout list (for this task only) and remove the first matching |
||||
* entry (subsequent entries remain untouched), even though the timeout has not |
||||
* triggered yet. |
||||
* |
||||
* @param handler callback function that would be called by the timeout |
||||
* @param arg callback argument that would be passed to handler |
||||
*/ |
||||
void |
||||
sys_untimeout(sys_timeout_handler handler, void *arg) |
||||
{ |
||||
struct sys_timeo *prev_t, *t; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
if (next_timeout == NULL) { |
||||
return; |
||||
} |
||||
|
||||
for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { |
||||
if ((t->h == handler) && (t->arg == arg)) { |
||||
/* We have a match */ |
||||
/* Unlink from previous in list */ |
||||
if (prev_t == NULL) { |
||||
next_timeout = t->next; |
||||
} else { |
||||
prev_t->next = t->next; |
||||
} |
||||
memp_free(MEMP_SYS_TIMEOUT, t); |
||||
return; |
||||
} |
||||
} |
||||
return; |
||||
} |
||||
|
||||
/**
|
||||
* @ingroup lwip_nosys |
||||
* Handle timeouts for NO_SYS==1 (i.e. without using |
||||
* tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout |
||||
* handler functions when timeouts expire. |
||||
* |
||||
* Must be called periodically from your main loop. |
||||
*/ |
||||
void |
||||
sys_check_timeouts(void) |
||||
{ |
||||
u32_t now; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
/* Process only timers expired at the start of the function. */ |
||||
now = sys_now(); |
||||
|
||||
do { |
||||
struct sys_timeo *tmptimeout; |
||||
sys_timeout_handler handler; |
||||
void *arg; |
||||
|
||||
PBUF_CHECK_FREE_OOSEQ(); |
||||
|
||||
tmptimeout = next_timeout; |
||||
if (tmptimeout == NULL) { |
||||
return; |
||||
} |
||||
|
||||
if (TIME_LESS_THAN(now, tmptimeout->time)) { |
||||
return; |
||||
} |
||||
|
||||
/* Timeout has expired */ |
||||
next_timeout = tmptimeout->next; |
||||
handler = tmptimeout->h; |
||||
arg = tmptimeout->arg; |
||||
current_timeout_due_time = tmptimeout->time; |
||||
#if LWIP_DEBUG_TIMERNAMES |
||||
if (handler != NULL) { |
||||
LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s t=%"U32_F" arg=%p\n", |
||||
tmptimeout->handler_name, sys_now() - tmptimeout->time, arg)); |
||||
} |
||||
#endif /* LWIP_DEBUG_TIMERNAMES */ |
||||
memp_free(MEMP_SYS_TIMEOUT, tmptimeout); |
||||
if (handler != NULL) { |
||||
handler(arg); |
||||
} |
||||
LWIP_TCPIP_THREAD_ALIVE(); |
||||
|
||||
/* Repeat until all expired timers have been called */ |
||||
} while (1); |
||||
} |
||||
|
||||
/** Rebase the timeout times to the current time.
|
||||
* This is necessary if sys_check_timeouts() hasn't been called for a long |
||||
* time (e.g. while saving energy) to prevent all timer functions of that |
||||
* period being called. |
||||
*/ |
||||
void |
||||
sys_restart_timeouts(void) |
||||
{ |
||||
u32_t now; |
||||
u32_t base; |
||||
struct sys_timeo *t; |
||||
|
||||
if (next_timeout == NULL) { |
||||
return; |
||||
} |
||||
|
||||
now = sys_now(); |
||||
base = next_timeout->time; |
||||
|
||||
for (t = next_timeout; t != NULL; t = t->next) { |
||||
t->time = (t->time - base) + now; |
||||
} |
||||
} |
||||
|
||||
/** Return the time left before the next timeout is due. If no timeouts are
|
||||
* enqueued, returns 0xffffffff |
||||
*/ |
||||
u32_t |
||||
sys_timeouts_sleeptime(void) |
||||
{ |
||||
u32_t now; |
||||
|
||||
LWIP_ASSERT_CORE_LOCKED(); |
||||
|
||||
if (next_timeout == NULL) { |
||||
return SYS_TIMEOUTS_SLEEPTIME_INFINITE; |
||||
} |
||||
now = sys_now(); |
||||
if (TIME_LESS_THAN(next_timeout->time, now)) { |
||||
return 0; |
||||
} else { |
||||
u32_t ret = (u32_t)(next_timeout->time - now); |
||||
LWIP_ASSERT("invalid sleeptime", ret <= LWIP_MAX_TIMEOUT); |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
#else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */ |
||||
/* Satisfy the TCP code which calls this function */ |
||||
void |
||||
tcp_timer_needed(void) |
||||
{ |
||||
} |
||||
#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */ |
||||
@ -0,0 +1,130 @@
|
||||
// lwip_pbuf.c — simplified packet buffer (PBUF_RAM only)
|
||||
#include "lwip_pbuf.h" |
||||
#include "../../lib/mem.h" |
||||
#include <string.h> |
||||
|
||||
#define PBUF_POOL_BUFSIZE 256 // not used with PBUF_RAM only
|
||||
|
||||
struct pbuf* pbuf_alloc(uint16_t layer, uint16_t length) |
||||
{ |
||||
uint16_t offset = (layer == PBUF_TRANSPORT) ? 54 : ((layer == PBUF_IP) ? 34 : (uint16_t)layer); |
||||
uint16_t total = sizeof(struct pbuf) + offset + length; |
||||
struct pbuf *p = u_malloc(total); |
||||
if (!p) return NULL; |
||||
p->next = NULL; |
||||
p->payload = (uint8_t*)p + sizeof(struct pbuf) + offset; |
||||
p->tot_len = length; |
||||
p->len = length; |
||||
p->ref = 1; |
||||
p->flags = 0; |
||||
p->type_internal = 0; |
||||
return p; |
||||
} |
||||
|
||||
void pbuf_free(struct pbuf *p) |
||||
{ |
||||
while (p) { |
||||
struct pbuf *next = p->next; |
||||
if (p->ref > 0) p->ref--; |
||||
if (p->ref == 0) u_free(p); |
||||
p = next; |
||||
} |
||||
} |
||||
|
||||
void pbuf_ref(struct pbuf *p) |
||||
{ |
||||
if (p) p->ref++; |
||||
} |
||||
|
||||
int pbuf_header(struct pbuf *p, int16_t increment) |
||||
{ |
||||
if (!p) return -1; |
||||
if (increment == 0) return 0; |
||||
if (increment < 0) { |
||||
if ((uint16_t)(-increment) > p->len) return -1; |
||||
} else { |
||||
if (p->payload < (void*)((uint8_t*)p + sizeof(struct pbuf))) return -1; |
||||
uint16_t avail = (uint16_t)((uint8_t*)p->payload - ((uint8_t*)p + sizeof(struct pbuf))); |
||||
if ((uint16_t)increment > avail) return -1; |
||||
} |
||||
p->payload = (uint8_t*)p->payload - increment; |
||||
p->len = (uint16_t)(p->len + increment); |
||||
p->tot_len = (uint16_t)(p->tot_len + increment); |
||||
return 0; |
||||
} |
||||
|
||||
int pbuf_header_force(struct pbuf *p, int16_t increment) |
||||
{ |
||||
if (!p) return -1; |
||||
if (increment < 0) { |
||||
if ((uint16_t)(-increment) > p->len) return -1; |
||||
} |
||||
p->payload = (uint8_t*)p->payload - increment; |
||||
p->len = (uint16_t)(p->len + increment); |
||||
p->tot_len = (uint16_t)(p->tot_len + increment); |
||||
return 0; |
||||
} |
||||
|
||||
void pbuf_chain(struct pbuf *head, struct pbuf *tail) |
||||
{ |
||||
if (!head) return; |
||||
struct pbuf *p = head; |
||||
while (p->next) p = p->next; |
||||
p->next = tail; |
||||
} |
||||
|
||||
struct pbuf* pbuf_dechain(struct pbuf *p) |
||||
{ |
||||
if (!p) return NULL; |
||||
struct pbuf *next = p->next; |
||||
p->next = NULL; |
||||
p->tot_len = p->len; |
||||
return next; |
||||
} |
||||
|
||||
uint16_t pbuf_clen(const struct pbuf *p) |
||||
{ |
||||
uint16_t count = 0; |
||||
while (p) { count++; p = p->next; } |
||||
return count; |
||||
} |
||||
|
||||
uint16_t pbuf_copy_partial(const struct pbuf *p, void *buf, uint16_t len, uint16_t offset) |
||||
{ |
||||
uint16_t copied = 0; |
||||
while (p && len > 0) { |
||||
if (offset >= p->len) { offset = (uint16_t)(offset - p->len); p = p->next; continue; } |
||||
uint16_t chunk = (uint16_t)(p->len - offset); |
||||
if (chunk > len) chunk = len; |
||||
memcpy((uint8_t*)buf + copied, (uint8_t*)p->payload + offset, chunk); |
||||
copied = (uint16_t)(copied + chunk); len = (uint16_t)(len - chunk); offset = 0; |
||||
p = p->next; |
||||
} |
||||
return copied; |
||||
} |
||||
|
||||
int pbuf_take(struct pbuf *p, const void *data, uint16_t len) |
||||
{ |
||||
return pbuf_take_at(p, data, len, 0); |
||||
} |
||||
|
||||
int pbuf_take_at(struct pbuf *p, const void *data, uint16_t len, uint16_t offset) |
||||
{ |
||||
uint16_t remaining = len; |
||||
const uint8_t *src = (const uint8_t*)data; |
||||
while (p && remaining > 0) { |
||||
if (offset >= p->len) { offset = (uint16_t)(offset - p->len); p = p->next; continue; } |
||||
uint16_t chunk = (uint16_t)(p->len - offset); |
||||
if (chunk > remaining) chunk = remaining; |
||||
memcpy((uint8_t*)p->payload + offset, src, chunk); |
||||
src += chunk; remaining = (uint16_t)(remaining - chunk); offset = 0; |
||||
p = p->next; |
||||
} |
||||
return (remaining == 0) ? 0 : -1; |
||||
} |
||||
|
||||
void* pbuf_get_contiguous(const struct pbuf *p, void *buf, size_t len, size_t offset) |
||||
{ |
||||
if (pbuf_copy_partial(p, buf, (uint16_t)len, (uint16_t)offset) == (uint16_t)len) return buf; |
||||
return NULL; |
||||
} |
||||
@ -0,0 +1,41 @@
|
||||
// lwip_pbuf.h — simplified packet buffer (PBUF_RAM only)
|
||||
#ifndef LWIP_PBUF_H |
||||
#define LWIP_PBUF_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
|
||||
enum pbuf_layer { |
||||
PBUF_TRANSPORT = 54, // 14(ETH) + 20(IP) + 20(TCP)
|
||||
PBUF_IP = 34, // 14(ETH) + 20(IP)
|
||||
PBUF_LINK = 14, // 14(ETH)
|
||||
PBUF_RAW_TX = 54, |
||||
PBUF_RAW = 0 |
||||
}; |
||||
|
||||
struct pbuf { |
||||
struct pbuf *next; |
||||
void *payload; |
||||
uint16_t tot_len; |
||||
uint16_t len; |
||||
uint8_t ref; |
||||
uint8_t flags; |
||||
uint8_t type_internal; |
||||
}; |
||||
|
||||
#define PBUF_FLAG_TCP_FIN 0x01U |
||||
|
||||
struct pbuf* pbuf_alloc(uint16_t layer, uint16_t length); |
||||
void pbuf_free(struct pbuf *p); |
||||
void pbuf_ref(struct pbuf *p); |
||||
int pbuf_header(struct pbuf *p, int16_t increment); |
||||
int pbuf_header_force(struct pbuf *p, int16_t increment); |
||||
void pbuf_chain(struct pbuf *head, struct pbuf *tail); |
||||
struct pbuf* pbuf_dechain(struct pbuf *p); |
||||
uint16_t pbuf_clen(const struct pbuf *p); |
||||
uint16_t pbuf_copy_partial(const struct pbuf *p, void *buf, uint16_t len, uint16_t offset); |
||||
int pbuf_take(struct pbuf *p, const void *data, uint16_t len); |
||||
int pbuf_take_at(struct pbuf *p, const void *data, uint16_t len, uint16_t offset); |
||||
void* pbuf_get_contiguous(const struct pbuf *p, void *buf, size_t len, size_t offset); |
||||
|
||||
#endif |
||||
@ -0,0 +1,230 @@
|
||||
// lwip_tcp.h — lwIP TCP public API (adapted for uTun)
|
||||
#ifndef LWIP_TCP_H |
||||
#define LWIP_TCP_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
#include "lwip_pbuf.h" |
||||
|
||||
struct UASYNC; |
||||
struct memory_pool; |
||||
struct tcp_seg; |
||||
struct lwip_tcp_ctx; |
||||
|
||||
enum tcp_state { |
||||
CLOSED = 0, |
||||
LISTEN = 1, |
||||
SYN_SENT = 2, |
||||
SYN_RCVD = 3, |
||||
ESTABLISHED = 4, |
||||
FIN_WAIT_1 = 5, |
||||
FIN_WAIT_2 = 6, |
||||
CLOSE_WAIT = 7, |
||||
CLOSING = 8, |
||||
LAST_ACK = 9, |
||||
TIME_WAIT = 10 |
||||
}; |
||||
|
||||
typedef uint16_t tcpwnd_size_t; |
||||
typedef uint16_t tcpflags_t; |
||||
|
||||
#define TCP_WRITE_FLAG_COPY 0x01 |
||||
#define TCP_WRITE_FLAG_MORE 0x02 |
||||
|
||||
// pcb flags
|
||||
#define TF_ACK_DELAY 0x01U |
||||
#define TF_ACK_NOW 0x02U |
||||
#define TF_INFR 0x04U |
||||
#define TF_CLOSEPEND 0x08U |
||||
#define TF_RXCLOSED 0x10U |
||||
#define TF_FIN 0x20U |
||||
#define TF_NODELAY 0x40U |
||||
#define TF_NAGLEMEMERR 0x80U |
||||
#define TF_RTO 0x0100U |
||||
|
||||
// input processing flags (not on pcb->flags)
|
||||
#define TF_RESET 0x08U |
||||
#define TF_CLOSED 0x10U |
||||
#define TF_GOT_FIN 0x20U |
||||
|
||||
// Error codes (LERR_ prefix to avoid conflicts with u_async.h and socket_compat.h)
|
||||
enum tcp_err_enum { |
||||
LERR_OK = 0, |
||||
LERR_MEM = -1, |
||||
LERR_BUF = -2, |
||||
LERR_TIMEOUT = -3, |
||||
LERR_RTE = -4, |
||||
LERR_INPROGRESS = -5, |
||||
LERR_VAL = -6, |
||||
LERR_WOULDBLOCK = -7, |
||||
LERR_USE = -8, |
||||
LERR_ALREADY = -9, |
||||
LERR_ISCONN = -10, |
||||
LERR_CONN = -11, |
||||
LERR_IF = -12, |
||||
LERR_ABRT = -13, |
||||
LERR_RST = -14, |
||||
LERR_CLSD = -15, |
||||
LERR_ARG = -16 |
||||
}; |
||||
typedef int err_t; |
||||
|
||||
// Forward declaration for callbacks
|
||||
struct tcp_pcb; |
||||
|
||||
// Callbacks
|
||||
typedef err_t (*tcp_accept_fn) (void *arg, struct tcp_pcb *newpcb, err_t err); |
||||
typedef err_t (*tcp_recv_fn) (void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); |
||||
typedef err_t (*tcp_sent_fn) (void *arg, struct tcp_pcb *pcb, uint16_t len); |
||||
typedef err_t (*tcp_poll_fn) (void *arg, struct tcp_pcb *pcb); |
||||
typedef void (*tcp_err_fn) (void *arg, err_t err); |
||||
typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *pcb, err_t err); |
||||
|
||||
// Output callback — called when TCP wants to send an IP packet
|
||||
typedef err_t (*tcp_output_fn)(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip); |
||||
|
||||
// PCB structures
|
||||
struct tcp_pcb { |
||||
struct tcp_pcb *next; |
||||
struct lwip_tcp_ctx *ctx; |
||||
void *callback_arg; |
||||
enum tcp_state state; |
||||
uint8_t prio; |
||||
uint16_t local_port; |
||||
uint16_t remote_port; |
||||
|
||||
uint32_t local_ip; // host byte order
|
||||
uint32_t remote_ip; // host byte order
|
||||
uint8_t ttl; |
||||
uint8_t tos; |
||||
|
||||
tcpflags_t flags; |
||||
uint8_t polltmr, pollinterval; |
||||
uint8_t last_timer; |
||||
uint32_t tmr; |
||||
|
||||
uint32_t rcv_nxt; |
||||
tcpwnd_size_t rcv_wnd; |
||||
tcpwnd_size_t rcv_ann_wnd; |
||||
uint32_t rcv_ann_right_edge; |
||||
|
||||
int16_t rtime; |
||||
uint16_t mss; |
||||
|
||||
uint32_t rttest; |
||||
uint32_t rtseq; |
||||
int16_t sa, sv; |
||||
int16_t rto; |
||||
uint8_t nrtx; |
||||
|
||||
uint8_t dupacks; |
||||
uint32_t lastack; |
||||
|
||||
tcpwnd_size_t cwnd; |
||||
tcpwnd_size_t ssthresh; |
||||
uint32_t rto_end; |
||||
|
||||
uint32_t snd_nxt; |
||||
uint32_t snd_wl1, snd_wl2; |
||||
uint32_t snd_lbb; |
||||
tcpwnd_size_t snd_wnd; |
||||
tcpwnd_size_t snd_wnd_max; |
||||
tcpwnd_size_t snd_buf; |
||||
uint16_t snd_queuelen; |
||||
|
||||
tcpwnd_size_t bytes_acked; |
||||
|
||||
struct tcp_seg *unsent; |
||||
struct tcp_seg *unacked; |
||||
struct tcp_seg *ooseq; |
||||
|
||||
struct pbuf *refused_data; |
||||
|
||||
struct tcp_pcb_listen *listener; |
||||
tcp_sent_fn sent; |
||||
tcp_recv_fn recv; |
||||
tcp_connected_fn connected; |
||||
tcp_poll_fn poll; |
||||
tcp_err_fn errf; |
||||
|
||||
uint32_t keep_idle; |
||||
uint8_t persist_cnt; |
||||
uint8_t persist_backoff; |
||||
uint8_t persist_probe; |
||||
uint8_t keep_cnt_sent; |
||||
}; |
||||
|
||||
struct tcp_pcb_listen { |
||||
struct tcp_pcb *next; |
||||
struct lwip_tcp_ctx *ctx; |
||||
void *callback_arg; |
||||
enum tcp_state state; |
||||
uint8_t prio; |
||||
uint16_t local_port; |
||||
uint32_t local_ip; |
||||
tcp_accept_fn accept; |
||||
}; |
||||
|
||||
// Context for the TCP module (one per tcp_proxy instance)
|
||||
struct lwip_tcp_ctx { |
||||
struct UASYNC *ua; |
||||
tcp_output_fn output; |
||||
void *output_arg; |
||||
void *timer; |
||||
|
||||
struct tcp_pcb *bound_pcbs; |
||||
struct tcp_pcb *listen_pcbs; |
||||
struct tcp_pcb *active_pcbs; |
||||
struct tcp_pcb *tw_pcbs; |
||||
|
||||
struct memory_pool *pcb_pool; |
||||
struct memory_pool *pcb_listen_pool; |
||||
struct memory_pool *seg_pool; |
||||
|
||||
uint32_t ticks; |
||||
uint8_t slowtmr_ctr; |
||||
uint16_t iss_seed; |
||||
uint16_t ip_id; |
||||
uint16_t port_seed; |
||||
}; |
||||
|
||||
// Lifecycle
|
||||
struct lwip_tcp_ctx* lwip_tcp_init(struct UASYNC *ua, tcp_output_fn output, void *output_arg); |
||||
void lwip_tcp_destroy(struct lwip_tcp_ctx *ctx); |
||||
|
||||
// Input: feed an IP packet payload (after IP header) to TCP
|
||||
void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p, |
||||
uint32_t src_ip, uint32_t dst_ip); |
||||
|
||||
// PCB operations
|
||||
struct tcp_pcb* tcp_new(struct lwip_tcp_ctx *ctx); |
||||
err_t tcp_bind(struct tcp_pcb *pcb, uint32_t ip, uint16_t port); |
||||
struct tcp_pcb* tcp_listen(struct tcp_pcb *pcb); |
||||
err_t tcp_connect(struct tcp_pcb *pcb, uint32_t ip, uint16_t port, tcp_connected_fn connected); |
||||
err_t tcp_write(struct tcp_pcb *pcb, const void *data, uint16_t len, uint8_t apiflags); |
||||
err_t tcp_output(struct tcp_pcb *pcb); |
||||
err_t tcp_close(struct tcp_pcb *pcb); |
||||
void tcp_abort(struct tcp_pcb *pcb); |
||||
void tcp_recved(struct tcp_pcb *pcb, uint16_t len); |
||||
|
||||
// Callback setters
|
||||
void tcp_arg (struct tcp_pcb *pcb, void *arg); |
||||
void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv); |
||||
void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent); |
||||
void tcp_err (struct tcp_pcb *pcb, tcp_err_fn errf); |
||||
void tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept); |
||||
void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, uint8_t interval); |
||||
|
||||
// Helpers
|
||||
#define tcp_mss(pcb) ((pcb)->mss) |
||||
#define tcp_sndbuf(pcb) ((pcb)->snd_buf) |
||||
#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen) |
||||
#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY) |
||||
#define tcp_nagle_enable(pcb) ((pcb)->flags &= (tcpflags_t)~TF_NODELAY) |
||||
#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0) |
||||
|
||||
#define tcp_set_flags(pcb, set_flags) ((pcb)->flags |= (tcpflags_t)(set_flags)) |
||||
#define tcp_clear_flags(pcb, clr_flags) ((pcb)->flags &= (tcpflags_t)~(clr_flags)) |
||||
#define tcp_is_flag_set(pcb, flag) (((pcb)->flags & (flag)) != 0) |
||||
|
||||
#endif |
||||
@ -0,0 +1,49 @@
|
||||
// lwip_tcp_opts.h — hardcoded TCP configuration (no #ifdefs)
|
||||
#ifndef LWIP_TCP_OPTS_H |
||||
#define LWIP_TCP_OPTS_H |
||||
|
||||
#define TCP_MSS 1460 |
||||
#define TCP_SND_BUF (16 * TCP_MSS) |
||||
#define TCP_WND (8 * TCP_MSS) |
||||
#define TCP_SND_QUEUELEN (4 * TCP_SND_BUF / TCP_MSS) |
||||
#define TCP_SNDLOWAT (2 * TCP_MSS < TCP_SND_BUF / 2 ? 2 * TCP_MSS : TCP_SND_BUF / 2) |
||||
|
||||
#define TCP_MAXRTX 12 |
||||
#define TCP_SYNMAXRTX 6 |
||||
#define TCP_TMR_INTERVAL 250 // ms for tcp_tmr
|
||||
#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL |
||||
#define TCP_SLOW_INTERVAL (2 * TCP_TMR_INTERVAL) |
||||
|
||||
#define TCP_FIN_WAIT_TIMEOUT 20000 // ms
|
||||
#define TCP_SYN_RCVD_TIMEOUT 20000 // ms
|
||||
#define TCP_MSL 60000 // ms
|
||||
#define TCP_OOSEQ_TIMEOUT 6 // x RTO
|
||||
|
||||
#define TCP_KEEPIDLE_DEFAULT 7200000 // ms (unused, no keepalive)
|
||||
#define TCP_KEEPINTVL_DEFAULT 75000 |
||||
#define TCP_KEEPCNT_DEFAULT 9 |
||||
|
||||
#define TCP_WND_SCALE 0 // disabled
|
||||
#define TCP_TIMESTAMPS 0 // disabled
|
||||
#define TCP_SACK_OUT 0 // disabled
|
||||
#define TCP_QUEUE_OOSEQ 1 |
||||
#define TCP_OVERSIZE 0 |
||||
#define TCP_LISTEN_BACKLOG 0 |
||||
#define TCP_KEEPALIVE 0 |
||||
#define TCP_CHECKSUM_ON_COPY 0 |
||||
#define TCP_CALCULATE_EFF_SEND_MSS 0 |
||||
|
||||
#define TCP_LOCAL_PORT_RANGE_START 0xc000 |
||||
#define TCP_LOCAL_PORT_RANGE_END 0xffff |
||||
|
||||
#if TCP_MSS > 536 |
||||
#define INITIAL_MSS 536 |
||||
#else |
||||
#define INITIAL_MSS TCP_MSS |
||||
#endif |
||||
|
||||
#define TCP_PRIO_MIN 1 |
||||
#define TCP_PRIO_NORMAL 64 |
||||
#define TCP_PRIO_MAX 127 |
||||
|
||||
#endif |
||||
@ -0,0 +1,239 @@
|
||||
// lwip_tcp_priv.h — lwIP TCP internal API
|
||||
#ifndef LWIP_TCP_PRIV_H |
||||
#define LWIP_TCP_PRIV_H |
||||
|
||||
#include "lwip_tcp.h" |
||||
#include <string.h> |
||||
|
||||
// Packed struct support
|
||||
#ifndef PACK_STRUCT_BEGIN |
||||
#define PACK_STRUCT_BEGIN |
||||
#endif |
||||
#ifndef PACK_STRUCT_END |
||||
#define PACK_STRUCT_END |
||||
#endif |
||||
#ifndef PACK_STRUCT_FIELD |
||||
#define PACK_STRUCT_FIELD(x) x |
||||
#endif |
||||
#ifndef PACK_STRUCT_STRUCT |
||||
#if defined(__GNUC__) || defined(__clang__) |
||||
#define PACK_STRUCT_STRUCT __attribute__((packed)) |
||||
#else |
||||
#define PACK_STRUCT_STRUCT |
||||
#endif |
||||
#endif |
||||
|
||||
#ifdef __linux__ |
||||
#include <arpa/inet.h> |
||||
#else |
||||
#define htons(x) ((((x) & 0xFF) << 8) | (((x) >> 8) & 0xFF)) |
||||
#define ntohs(x) htons(x) |
||||
#define htonl(x) ((((x) & 0xFF) << 24) | (((x) & 0xFF00) << 8) | (((x) >> 8) & 0xFF00) | (((x) >> 24) & 0xFF)) |
||||
#define ntohl(x) htonl(x) |
||||
#endif |
||||
|
||||
// TCP header (wire format)
|
||||
PACK_STRUCT_BEGIN |
||||
struct tcp_hdr { |
||||
PACK_STRUCT_FIELD(uint16_t src); |
||||
PACK_STRUCT_FIELD(uint16_t dest); |
||||
PACK_STRUCT_FIELD(uint32_t seqno); |
||||
PACK_STRUCT_FIELD(uint32_t ackno); |
||||
PACK_STRUCT_FIELD(uint16_t _hdrlen_rsvd_flags); |
||||
PACK_STRUCT_FIELD(uint16_t wnd); |
||||
PACK_STRUCT_FIELD(uint16_t chksum); |
||||
PACK_STRUCT_FIELD(uint16_t urgp); |
||||
} PACK_STRUCT_STRUCT; |
||||
PACK_STRUCT_END |
||||
|
||||
#define TCP_FIN 0x01U |
||||
#define TCP_SYN 0x02U |
||||
#define TCP_RST 0x04U |
||||
#define TCP_PSH 0x08U |
||||
#define TCP_ACK 0x10U |
||||
#define TCP_URG 0x20U |
||||
#define TCP_ECE 0x40U |
||||
#define TCP_CWR 0x80U |
||||
#define TCP_FLAGS 0x3fU |
||||
|
||||
#define TCPH_HDRLEN(phdr) ((uint16_t)(ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)) |
||||
#define TCPH_HDRLEN_BYTES(phdr) ((uint8_t)(TCPH_HDRLEN(phdr) << 2)) |
||||
#define TCPH_FLAGS(phdr) ((uint8_t)(ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)) |
||||
|
||||
#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) |
||||
#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & htons((uint16_t)~TCP_FLAGS)) | htons(flags)) |
||||
#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons((uint16_t)(((uint16_t)(len) << 12) | (flags))) |
||||
|
||||
#define TCPH_SET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags)) |
||||
#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~htons(flags)) |
||||
|
||||
// TCP header length
|
||||
#define TCP_HLEN 20 |
||||
|
||||
// Sequence number macros
|
||||
#define TCP_SEQ_LT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) < 0) |
||||
#define TCP_SEQ_LEQ(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) <= 0) |
||||
#define TCP_SEQ_GT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) > 0) |
||||
#define TCP_SEQ_GEQ(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) >= 0) |
||||
#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) |
||||
|
||||
// TCP options
|
||||
#define TCP_OPT_END 0 |
||||
#define TCP_OPT_NOP 1 |
||||
#define TCP_OPT_MSS 2 |
||||
#define TCP_OPT_MSS_LEN 4 |
||||
|
||||
// tcp_seg flags
|
||||
#define TF_SEG_OPTS_MSS 0x01U |
||||
|
||||
// tcp_seg structure
|
||||
struct tcp_seg { |
||||
struct tcp_seg *next; |
||||
struct pbuf *p; |
||||
uint16_t len; |
||||
uint8_t flags; |
||||
struct tcp_hdr *tcphdr; |
||||
}; |
||||
|
||||
#define TCP_TCPLEN(seg) ((seg)->len + (((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0) ? 1U : 0U)) |
||||
|
||||
// Window macros
|
||||
#define TCPWND16(x) (x) |
||||
#define TCP_WND_MAX(pcb) TCP_WND |
||||
#define RCV_WND_SCALE(pcb, wnd) (wnd) |
||||
#define SND_WND_SCALE(pcb, wnd) (wnd) |
||||
#define TCP_WND_INC(wnd, inc) do { \ |
||||
if ((tcpwnd_size_t)(wnd + inc) >= wnd) { wnd = (tcpwnd_size_t)(wnd + inc); } \
|
||||
else { wnd = (tcpwnd_size_t)-1; } \
|
||||
} while(0) |
||||
|
||||
// TCP_STATE_IS_CLOSING
|
||||
#define TCP_STATE_IS_CLOSING(state) ((state) >= FIN_WAIT_1) |
||||
|
||||
// Nagle
|
||||
#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ |
||||
((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
|
||||
(((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
|
||||
((tpcb)->unsent->len >= (tpcb)->mss))) || \
|
||||
(tcp_sndbuf(tpcb) == 0 || tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) ? 1 : 0) |
||||
#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : LERR_OK) |
||||
|
||||
// Callback event macros
|
||||
#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) \ |
||||
do { if((lpcb)->accept != NULL) (ret) = (lpcb)->accept((arg),(pcb),(err)); else (ret) = LERR_ARG; } while(0) |
||||
|
||||
#define TCP_EVENT_SENT(pcb,space,ret) \ |
||||
do { if((pcb)->sent != NULL) (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); else (ret) = LERR_OK; } while(0) |
||||
|
||||
#define TCP_EVENT_RECV(pcb,p,err,ret) \ |
||||
do { if((pcb)->recv != NULL) { (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err)); } \
|
||||
else { (ret) = LERR_OK; if(p) pbuf_free(p); } } while(0) |
||||
|
||||
#define TCP_EVENT_CLOSED(pcb,ret) \ |
||||
do { if(((pcb)->recv != NULL)) { (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,LERR_OK); } \
|
||||
else { (ret) = LERR_OK; } } while(0) |
||||
|
||||
#define TCP_EVENT_CONNECTED(pcb,err,ret) \ |
||||
do { if((pcb)->connected != NULL) (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); else (ret) = LERR_OK; } while(0) |
||||
|
||||
#define TCP_EVENT_POLL(pcb,ret) \ |
||||
do { if((pcb)->poll != NULL) (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); else (ret) = LERR_OK; } while(0) |
||||
|
||||
#define TCP_EVENT_ERR(last_state,errf,arg,err) \ |
||||
do { (void)(last_state); if((errf) != NULL) (errf)((arg),(err)); } while(0) |
||||
|
||||
// tcp_ack / tcp_ack_now
|
||||
#define tcp_ack(pcb) do { \ |
||||
if((pcb)->flags & TF_ACK_DELAY) { tcp_clear_flags(pcb, TF_ACK_DELAY); tcp_ack_now(pcb); } \
|
||||
else { tcp_set_flags(pcb, TF_ACK_DELAY); } \
|
||||
} while(0) |
||||
|
||||
#define tcp_ack_now(pcb) tcp_set_flags(pcb, TF_ACK_NOW) |
||||
|
||||
// TCP timer intervals
|
||||
#define TCP_TMR_INTERVAL_MS TCP_TMR_INTERVAL |
||||
#define TCP_FAST_INTERVAL_MS TCP_FAST_INTERVAL |
||||
#define TCP_SLOW_INTERVAL_MS TCP_SLOW_INTERVAL |
||||
#define TCP_FIN_WAIT_TIMEOUT_MS TCP_FIN_WAIT_TIMEOUT |
||||
#define TCP_SYN_RCVD_TIMEOUT_MS TCP_SYN_RCVD_TIMEOUT |
||||
#define TCP_MSL_MS TCP_MSL |
||||
|
||||
// build MSS option
|
||||
#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) |
||||
|
||||
// IP header
|
||||
PACK_STRUCT_BEGIN |
||||
struct ip_hdr { |
||||
uint8_t vhl; |
||||
uint8_t tos; |
||||
uint16_t len; |
||||
uint16_t id; |
||||
uint16_t offset; |
||||
uint8_t ttl; |
||||
uint8_t proto; |
||||
uint16_t chksum; |
||||
uint32_t src; |
||||
uint32_t dst; |
||||
} PACK_STRUCT_STRUCT; |
||||
PACK_STRUCT_END |
||||
|
||||
#define IP_PROTO_TCP 6 |
||||
|
||||
// TCP backoff arrays
|
||||
extern const uint8_t tcp_backoff[13]; |
||||
extern const uint8_t tcp_persist_backoff[7]; |
||||
|
||||
// Internal functions
|
||||
err_t tcp_send_empty_ack(struct tcp_pcb *pcb); |
||||
err_t tcp_send_fin(struct tcp_pcb *pcb); |
||||
err_t tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags); |
||||
void tcp_rst(struct tcp_pcb *pcb, uint32_t seqno, uint32_t ackno, |
||||
uint32_t local_ip, uint32_t remote_ip, |
||||
uint16_t local_port, uint16_t remote_port); |
||||
err_t tcp_rexmit(struct tcp_pcb *pcb); |
||||
err_t tcp_rexmit_rto_prepare(struct tcp_pcb *pcb); |
||||
void tcp_rexmit_rto_commit(struct tcp_pcb *pcb); |
||||
void tcp_rexmit_rto(struct tcp_pcb *pcb); |
||||
void tcp_rexmit_fast(struct tcp_pcb *pcb); |
||||
void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); |
||||
err_t tcp_keepalive(struct tcp_pcb *pcb); |
||||
err_t tcp_zero_window_probe(struct tcp_pcb *pcb); |
||||
err_t tcp_split_unsent_seg(struct tcp_pcb *pcb, uint16_t split); |
||||
void tcp_trigger_input_pcb_close(void); |
||||
err_t tcp_process_refused_data(struct tcp_pcb *pcb); |
||||
|
||||
// Segment management
|
||||
struct tcp_pcb* tcp_alloc(struct lwip_tcp_ctx *ctx, uint8_t prio); |
||||
void tcp_free(struct tcp_pcb *pcb); |
||||
void tcp_pcb_purge(struct tcp_pcb *pcb); |
||||
void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); |
||||
void tcp_segs_free(struct tcp_seg *seg); |
||||
void tcp_seg_free(struct tcp_seg *seg); |
||||
struct tcp_seg* tcp_seg_copy(struct tcp_seg *seg); |
||||
void tcp_free_ooseq(struct tcp_pcb *pcb); |
||||
|
||||
// Timer internal
|
||||
void tcp_fasttmr(struct lwip_tcp_ctx *ctx); |
||||
void tcp_slowtmr(struct lwip_tcp_ctx *ctx); |
||||
void tcp_txnow(struct lwip_tcp_ctx *ctx); |
||||
|
||||
// Utils
|
||||
uint32_t tcp_next_iss(struct tcp_pcb *pcb); |
||||
uint16_t tcp_checksum(const void *data, uint16_t len); |
||||
uint16_t tcp_ip_checksum(const struct ip_hdr *iph); |
||||
|
||||
// PCB list management
|
||||
#define TCP_REG(pcbs, npcb) do { \ |
||||
(npcb)->next = *(pcbs); *(pcbs) = (npcb); \
|
||||
} while(0) |
||||
|
||||
#define TCP_RMV(pcbs, npcb) do { \ |
||||
if(*(pcbs) == (npcb)) { *(pcbs) = (*pcbs)->next; } \
|
||||
else { struct tcp_pcb *tmp; for(tmp = *(pcbs); tmp != NULL; tmp = tmp->next) { if(tmp->next == (npcb)) { tmp->next = (npcb)->next; break; } } } \
|
||||
(npcb)->next = NULL; \
|
||||
} while(0) |
||||
|
||||
#define TCP_REG_ACTIVE(ctx, npcb) TCP_REG(&(ctx)->active_pcbs, npcb) |
||||
#define TCP_RMV_ACTIVE(ctx, npcb) TCP_RMV(&(ctx)->active_pcbs, npcb) |
||||
|
||||
#endif |
||||
@ -1,48 +0,0 @@
|
||||
// uip-conf.h — Project-specific uIP configuration for uTun TCP Proxy
|
||||
#ifndef __UIP_CONF_H__ |
||||
#define __UIP_CONF_H__ |
||||
|
||||
#include <stdint.h> |
||||
|
||||
// uIP base types
|
||||
typedef uint8_t u8_t; |
||||
typedef uint16_t u16_t; |
||||
typedef unsigned short uip_stats_t; |
||||
|
||||
// Buffer size: full MTU (no Ethernet overhead thanks to TUN)
|
||||
#define UIP_CONF_BUFFER_SIZE 1500 |
||||
|
||||
// No link-layer header — TUN gives raw IP packets directly
|
||||
#define UIP_CONF_LLH_LEN 0 |
||||
|
||||
// Max simultaneous TCP connections (must be < 256, uip.c uses u8_t loop counter)
|
||||
#define UIP_CONF_MAX_CONNECTIONS 128 |
||||
|
||||
// Listening ports (wildcard accept used, minimal array)
|
||||
#define UIP_CONF_MAX_LISTENPORTS 4 |
||||
|
||||
// Only IPv4
|
||||
#define UIP_CONF_IPV6 0 |
||||
|
||||
// TCP only (no UDP)
|
||||
#define UIP_CONF_UDP 0 |
||||
#define UIP_CONF_BROADCAST 0 |
||||
|
||||
// Byte order
|
||||
#define UIP_CONF_BYTE_ORDER UIP_LITTLE_ENDIAN |
||||
|
||||
// Features
|
||||
#define UIP_CONF_STATISTICS 1 |
||||
#define UIP_CONF_LOGGING 0 |
||||
|
||||
// TCP window: advertise full buffer
|
||||
#define UIP_CONF_RECEIVE_WINDOW 1500 |
||||
|
||||
// Application callback — defined in tcp_proxy.c
|
||||
void tcp_proxy_appcall(void); |
||||
#define UIP_APPCALL tcp_proxy_appcall |
||||
|
||||
// Per-connection application state: pointer to struct proxy_conn
|
||||
typedef void* uip_tcp_appstate_t; |
||||
|
||||
#endif // __UIP_CONF_H__
|
||||
@ -1,138 +0,0 @@
|
||||
/**
|
||||
* \addtogroup uip |
||||
* {@ |
||||
*/ |
||||
|
||||
/**
|
||||
* \defgroup uiparch Architecture specific uIP functions |
||||
* @{ |
||||
* |
||||
* The functions in the architecture specific module implement the IP |
||||
* check sum and 32-bit additions. |
||||
* |
||||
* The IP checksum calculation is the most computationally expensive |
||||
* operation in the TCP/IP stack and it therefore pays off to |
||||
* implement this in efficient assembler. The purpose of the uip-arch |
||||
* module is to let the checksum functions to be implemented in |
||||
* architecture specific assembler. |
||||
* |
||||
*/ |
||||
|
||||
/**
|
||||
* \file |
||||
* Declarations of architecture specific functions. |
||||
* \author Adam Dunkels <adam@dunkels.com> |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2001, Adam Dunkels. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions |
||||
* are met: |
||||
* 1. Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* 3. The name of the author may not be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This file is part of the uIP TCP/IP stack. |
||||
* |
||||
* $Id: uip_arch.h,v 1.2 2006/06/07 09:15:19 adam Exp $ |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __UIP_ARCH_H__ |
||||
#define __UIP_ARCH_H__ |
||||
|
||||
#include "uip.h" |
||||
|
||||
/**
|
||||
* Carry out a 32-bit addition. |
||||
* |
||||
* Because not all architectures for which uIP is intended has native |
||||
* 32-bit arithmetic, uIP uses an external C function for doing the |
||||
* required 32-bit additions in the TCP protocol processing. This |
||||
* function should add the two arguments and place the result in the |
||||
* global variable uip_acc32. |
||||
* |
||||
* \note The 32-bit integer pointed to by the op32 parameter and the |
||||
* result in the uip_acc32 variable are in network byte order (big |
||||
* endian). |
||||
* |
||||
* \param op32 A pointer to a 4-byte array representing a 32-bit |
||||
* integer in network byte order (big endian). |
||||
* |
||||
* \param op16 A 16-bit integer in host byte order. |
||||
*/ |
||||
void uip_add32(u8_t *op32, u16_t op16); |
||||
|
||||
/**
|
||||
* Calculate the Internet checksum over a buffer. |
||||
* |
||||
* The Internet checksum is the one's complement of the one's |
||||
* complement sum of all 16-bit words in the buffer. |
||||
* |
||||
* See RFC1071. |
||||
* |
||||
* \note This function is not called in the current version of uIP, |
||||
* but future versions might make use of it. |
||||
* |
||||
* \param buf A pointer to the buffer over which the checksum is to be |
||||
* computed. |
||||
* |
||||
* \param len The length of the buffer over which the checksum is to |
||||
* be computed. |
||||
* |
||||
* \return The Internet checksum of the buffer. |
||||
*/ |
||||
u16_t uip_chksum(u16_t *buf, u16_t len); |
||||
|
||||
/**
|
||||
* Calculate the IP header checksum of the packet header in uip_buf. |
||||
* |
||||
* The IP header checksum is the Internet checksum of the 20 bytes of |
||||
* the IP header. |
||||
* |
||||
* \return The IP header checksum of the IP header in the uip_buf |
||||
* buffer. |
||||
*/ |
||||
u16_t uip_ipchksum(void); |
||||
|
||||
/**
|
||||
* Calculate the TCP checksum of the packet in uip_buf and uip_appdata. |
||||
* |
||||
* The TCP checksum is the Internet checksum of data contents of the |
||||
* TCP segment, and a pseudo-header as defined in RFC793. |
||||
* |
||||
* \note The uip_appdata pointer that points to the packet data may |
||||
* point anywhere in memory, so it is not possible to simply calculate |
||||
* the Internet checksum of the contents of the uip_buf buffer. |
||||
* |
||||
* \return The TCP checksum of the TCP segment in uip_buf and pointed |
||||
* to by uip_appdata. |
||||
*/ |
||||
u16_t uip_tcpchksum(void); |
||||
|
||||
u16_t uip_udpchksum(void); |
||||
|
||||
/** @} */ |
||||
/** @} */ |
||||
|
||||
#endif /* __UIP_ARCH_H__ */ |
||||
@ -1,539 +0,0 @@
|
||||
/**
|
||||
* \defgroup uipopt Configuration options for uIP |
||||
* @{ |
||||
* |
||||
* uIP is configured using the per-project configuration file |
||||
* uipopt.h. This file contains all compile-time options for uIP and |
||||
* should be tweaked to match each specific project. The uIP |
||||
* distribution contains a documented example "uipopt.h" that can be |
||||
* copied and modified for each project. |
||||
* |
||||
* \note Most of the configuration options in the uipopt.h should not |
||||
* be changed, but rather the per-project uip-conf.h file. |
||||
*/ |
||||
|
||||
/**
|
||||
* \file |
||||
* Configuration options for uIP. |
||||
* \author Adam Dunkels <adam@dunkels.com> |
||||
* |
||||
* This file is used for tweaking various configuration options for |
||||
* uIP. You should make a copy of this file into one of your project's |
||||
* directories instead of editing this example "uipopt.h" file that |
||||
* comes with the uIP distribution. |
||||
*/ |
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2003, Adam Dunkels. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions |
||||
* are met: |
||||
* 1. Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* 3. The name of the author may not be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This file is part of the uIP TCP/IP stack. |
||||
* |
||||
* $Id: uipopt.h,v 1.4 2006/06/12 08:00:31 adam Exp $ |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __UIPOPT_H__ |
||||
#define __UIPOPT_H__ |
||||
|
||||
#ifndef UIP_LITTLE_ENDIAN |
||||
#define UIP_LITTLE_ENDIAN 3412 |
||||
#endif /* UIP_LITTLE_ENDIAN */ |
||||
#ifndef UIP_BIG_ENDIAN |
||||
#define UIP_BIG_ENDIAN 1234 |
||||
#endif /* UIP_BIG_ENDIAN */ |
||||
|
||||
#include "uip-conf.h" |
||||
|
||||
/*------------------------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* \name Static configuration options |
||||
* @{ |
||||
* |
||||
* These configuration options can be used for setting the IP address |
||||
* settings statically, but only if UIP_FIXEDADDR is set to 1. The |
||||
* configuration options for a specific node includes IP address, |
||||
* netmask and default router as well as the Ethernet address. The |
||||
* netmask, default router and Ethernet address are appliciable only |
||||
* if uIP should be run over Ethernet. |
||||
* |
||||
* All of these should be changed to suit your project. |
||||
*/ |
||||
|
||||
/**
|
||||
* Determines if uIP should use a fixed IP address or not. |
||||
* |
||||
* If uIP should use a fixed IP address, the settings are set in the |
||||
* uipopt.h file. If not, the macros uip_sethostaddr(), |
||||
* uip_setdraddr() and uip_setnetmask() should be used instead. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#define UIP_FIXEDADDR 0 |
||||
|
||||
/**
|
||||
* Ping IP address asignment. |
||||
* |
||||
* uIP uses a "ping" packets for setting its own IP address if this |
||||
* option is set. If so, uIP will start with an empty IP address and |
||||
* the destination IP address of the first incoming "ping" (ICMP echo) |
||||
* packet will be used for setting the hosts IP address. |
||||
* |
||||
* \note This works only if UIP_FIXEDADDR is 0. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifdef UIP_CONF_PINGADDRCONF |
||||
#define UIP_PINGADDRCONF UIP_CONF_PINGADDRCONF |
||||
#else /* UIP_CONF_PINGADDRCONF */ |
||||
#define UIP_PINGADDRCONF 0 |
||||
#endif /* UIP_CONF_PINGADDRCONF */ |
||||
|
||||
|
||||
/**
|
||||
* Specifies if the uIP ARP module should be compiled with a fixed |
||||
* Ethernet MAC address or not. |
||||
* |
||||
* If this configuration option is 0, the macro uip_setethaddr() can |
||||
* be used to specify the Ethernet address at run-time. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#define UIP_FIXEDETHADDR 0 |
||||
|
||||
/** @} */ |
||||
/*------------------------------------------------------------------------------*/ |
||||
/**
|
||||
* \name IP configuration options |
||||
* @{ |
||||
* |
||||
*/ |
||||
/**
|
||||
* The IP TTL (time to live) of IP packets sent by uIP. |
||||
* |
||||
* This should normally not be changed. |
||||
*/ |
||||
#define UIP_TTL 64 |
||||
|
||||
/**
|
||||
* Turn on support for IP packet reassembly. |
||||
* |
||||
* uIP supports reassembly of fragmented IP packets. This features |
||||
* requires an additonal amount of RAM to hold the reassembly buffer |
||||
* and the reassembly code size is approximately 700 bytes. The |
||||
* reassembly buffer is of the same size as the uip_buf buffer |
||||
* (configured by UIP_BUFSIZE). |
||||
* |
||||
* \note IP packet reassembly is not heavily tested. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#define UIP_REASSEMBLY 0 |
||||
|
||||
/**
|
||||
* The maximum time an IP fragment should wait in the reassembly |
||||
* buffer before it is dropped. |
||||
* |
||||
*/ |
||||
#define UIP_REASS_MAXAGE 40 |
||||
|
||||
/** @} */ |
||||
|
||||
/*------------------------------------------------------------------------------*/ |
||||
/**
|
||||
* \name UDP configuration options |
||||
* @{ |
||||
*/ |
||||
|
||||
/**
|
||||
* Toggles wether UDP support should be compiled in or not. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifdef UIP_CONF_UDP |
||||
#define UIP_UDP UIP_CONF_UDP |
||||
#else /* UIP_CONF_UDP */ |
||||
#define UIP_UDP 0 |
||||
#endif /* UIP_CONF_UDP */ |
||||
|
||||
/**
|
||||
* Toggles if UDP checksums should be used or not. |
||||
* |
||||
* \note Support for UDP checksums is currently not included in uIP, |
||||
* so this option has no function. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifdef UIP_CONF_UDP_CHECKSUMS |
||||
#define UIP_UDP_CHECKSUMS UIP_CONF_UDP_CHECKSUMS |
||||
#else |
||||
#define UIP_UDP_CHECKSUMS 0 |
||||
#endif |
||||
|
||||
/**
|
||||
* The maximum amount of concurrent UDP connections. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifdef UIP_CONF_UDP_CONNS |
||||
#define UIP_UDP_CONNS UIP_CONF_UDP_CONNS |
||||
#else /* UIP_CONF_UDP_CONNS */ |
||||
#define UIP_UDP_CONNS 10 |
||||
#endif /* UIP_CONF_UDP_CONNS */ |
||||
|
||||
/**
|
||||
* The name of the function that should be called when UDP datagrams arrive. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
|
||||
|
||||
/** @} */ |
||||
/*------------------------------------------------------------------------------*/ |
||||
/**
|
||||
* \name TCP configuration options |
||||
* @{ |
||||
*/ |
||||
|
||||
/**
|
||||
* Determines if support for opening connections from uIP should be |
||||
* compiled in. |
||||
* |
||||
* If the applications that are running on top of uIP for this project |
||||
* do not need to open outgoing TCP connections, this configration |
||||
* option can be turned off to reduce the code size of uIP. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#define UIP_ACTIVE_OPEN 1 |
||||
|
||||
/**
|
||||
* The maximum number of simultaneously open TCP connections. |
||||
* |
||||
* Since the TCP connections are statically allocated, turning this |
||||
* configuration knob down results in less RAM used. Each TCP |
||||
* connection requires approximatly 30 bytes of memory. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifndef UIP_CONF_MAX_CONNECTIONS |
||||
#define UIP_CONNS 10 |
||||
#else /* UIP_CONF_MAX_CONNECTIONS */ |
||||
#define UIP_CONNS UIP_CONF_MAX_CONNECTIONS |
||||
#endif /* UIP_CONF_MAX_CONNECTIONS */ |
||||
|
||||
|
||||
/**
|
||||
* The maximum number of simultaneously listening TCP ports. |
||||
* |
||||
* Each listening TCP port requires 2 bytes of memory. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifndef UIP_CONF_MAX_LISTENPORTS |
||||
#define UIP_LISTENPORTS 20 |
||||
#else /* UIP_CONF_MAX_LISTENPORTS */ |
||||
#define UIP_LISTENPORTS UIP_CONF_MAX_LISTENPORTS |
||||
#endif /* UIP_CONF_MAX_LISTENPORTS */ |
||||
|
||||
/**
|
||||
* Determines if support for TCP urgent data notification should be |
||||
* compiled in. |
||||
* |
||||
* Urgent data (out-of-band data) is a rarely used TCP feature that |
||||
* very seldom would be required. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#define UIP_URGDATA 0 |
||||
|
||||
/**
|
||||
* The initial retransmission timeout counted in timer pulses. |
||||
* |
||||
* This should not be changed. |
||||
*/ |
||||
#define UIP_RTO 3 |
||||
|
||||
/**
|
||||
* The maximum number of times a segment should be retransmitted |
||||
* before the connection should be aborted. |
||||
* |
||||
* This should not be changed. |
||||
*/ |
||||
#define UIP_MAXRTX 8 |
||||
|
||||
/**
|
||||
* The maximum number of times a SYN segment should be retransmitted |
||||
* before a connection request should be deemed to have been |
||||
* unsuccessful. |
||||
* |
||||
* This should not need to be changed. |
||||
*/ |
||||
#define UIP_MAXSYNRTX 5 |
||||
|
||||
/**
|
||||
* The TCP maximum segment size. |
||||
* |
||||
* This is should not be to set to more than |
||||
* UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. |
||||
*/ |
||||
#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) |
||||
|
||||
/**
|
||||
* The size of the advertised receiver's window. |
||||
* |
||||
* Should be set low (i.e., to the size of the uip_buf buffer) is the |
||||
* application is slow to process incoming data, or high (32768 bytes) |
||||
* if the application processes data quickly. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifndef UIP_CONF_RECEIVE_WINDOW |
||||
#define UIP_RECEIVE_WINDOW UIP_TCP_MSS |
||||
#else |
||||
#define UIP_RECEIVE_WINDOW UIP_CONF_RECEIVE_WINDOW |
||||
#endif |
||||
|
||||
/**
|
||||
* How long a connection should stay in the TIME_WAIT state. |
||||
* |
||||
* This configiration option has no real implication, and it should be |
||||
* left untouched. |
||||
*/ |
||||
#define UIP_TIME_WAIT_TIMEOUT 120 |
||||
|
||||
|
||||
/** @} */ |
||||
/*------------------------------------------------------------------------------*/ |
||||
/**
|
||||
* \name ARP configuration options |
||||
* @{ |
||||
*/ |
||||
|
||||
/**
|
||||
* The size of the ARP table. |
||||
* |
||||
* This option should be set to a larger value if this uIP node will |
||||
* have many connections from the local network. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifdef UIP_CONF_ARPTAB_SIZE |
||||
#define UIP_ARPTAB_SIZE UIP_CONF_ARPTAB_SIZE |
||||
#else |
||||
#define UIP_ARPTAB_SIZE 8 |
||||
#endif |
||||
|
||||
/**
|
||||
* The maxium age of ARP table entries measured in 10ths of seconds. |
||||
* |
||||
* An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD |
||||
* default). |
||||
*/ |
||||
#define UIP_ARP_MAXAGE 120 |
||||
|
||||
/** @} */ |
||||
|
||||
/*------------------------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* \name General configuration options |
||||
* @{ |
||||
*/ |
||||
|
||||
/**
|
||||
* The size of the uIP packet buffer. |
||||
* |
||||
* The uIP packet buffer should not be smaller than 60 bytes, and does |
||||
* not need to be larger than 1500 bytes. Lower size results in lower |
||||
* TCP throughput, larger size results in higher TCP throughput. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifndef UIP_CONF_BUFFER_SIZE |
||||
#define UIP_BUFSIZE 400 |
||||
#else /* UIP_CONF_BUFFER_SIZE */ |
||||
#define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE |
||||
#endif /* UIP_CONF_BUFFER_SIZE */ |
||||
|
||||
|
||||
/**
|
||||
* Determines if statistics support should be compiled in. |
||||
* |
||||
* The statistics is useful for debugging and to show the user. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifndef UIP_CONF_STATISTICS |
||||
#define UIP_STATISTICS 0 |
||||
#else /* UIP_CONF_STATISTICS */ |
||||
#define UIP_STATISTICS UIP_CONF_STATISTICS |
||||
#endif /* UIP_CONF_STATISTICS */ |
||||
|
||||
/**
|
||||
* Determines if logging of certain events should be compiled in. |
||||
* |
||||
* This is useful mostly for debugging. The function uip_log() |
||||
* must be implemented to suit the architecture of the project, if |
||||
* logging is turned on. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifndef UIP_CONF_LOGGING |
||||
#define UIP_LOGGING 0 |
||||
#else /* UIP_CONF_LOGGING */ |
||||
#define UIP_LOGGING UIP_CONF_LOGGING |
||||
#endif /* UIP_CONF_LOGGING */ |
||||
|
||||
/**
|
||||
* Broadcast support. |
||||
* |
||||
* This flag configures IP broadcast support. This is useful only |
||||
* together with UDP. |
||||
* |
||||
* \hideinitializer |
||||
* |
||||
*/ |
||||
#ifndef UIP_CONF_BROADCAST |
||||
#define UIP_BROADCAST 0 |
||||
#else /* UIP_CONF_BROADCAST */ |
||||
#define UIP_BROADCAST UIP_CONF_BROADCAST |
||||
#endif /* UIP_CONF_BROADCAST */ |
||||
|
||||
/**
|
||||
* Print out a uIP log message. |
||||
* |
||||
* This function must be implemented by the module that uses uIP, and |
||||
* is called by uIP whenever a log message is generated. |
||||
*/ |
||||
void uip_log(char *msg); |
||||
|
||||
/**
|
||||
* The link level header length. |
||||
* |
||||
* This is the offset into the uip_buf where the IP header can be |
||||
* found. For Ethernet, this should be set to 14. For SLIP, this |
||||
* should be set to 0. |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifdef UIP_CONF_LLH_LEN |
||||
#define UIP_LLH_LEN UIP_CONF_LLH_LEN |
||||
#else /* UIP_CONF_LLH_LEN */ |
||||
#define UIP_LLH_LEN 14 |
||||
#endif /* UIP_CONF_LLH_LEN */ |
||||
|
||||
/** @} */ |
||||
/*------------------------------------------------------------------------------*/ |
||||
/**
|
||||
* \name CPU architecture configuration |
||||
* @{ |
||||
* |
||||
* The CPU architecture configuration is where the endianess of the |
||||
* CPU on which uIP is to be run is specified. Most CPUs today are |
||||
* little endian, and the most notable exception are the Motorolas |
||||
* which are big endian. The BYTE_ORDER macro should be changed to |
||||
* reflect the CPU architecture on which uIP is to be run. |
||||
*/ |
||||
|
||||
/**
|
||||
* The byte order of the CPU architecture on which uIP is to be run. |
||||
* |
||||
* This option can be either BIG_ENDIAN (Motorola byte order) or |
||||
* LITTLE_ENDIAN (Intel byte order). |
||||
* |
||||
* \hideinitializer |
||||
*/ |
||||
#ifdef UIP_CONF_BYTE_ORDER |
||||
#define UIP_BYTE_ORDER UIP_CONF_BYTE_ORDER |
||||
#else /* UIP_CONF_BYTE_ORDER */ |
||||
#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN |
||||
#endif /* UIP_CONF_BYTE_ORDER */ |
||||
|
||||
/** @} */ |
||||
/*------------------------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* \name Appication specific configurations |
||||
* @{ |
||||
* |
||||
* An uIP application is implemented using a single application |
||||
* function that is called by uIP whenever a TCP/IP event occurs. The |
||||
* name of this function must be registered with uIP at compile time |
||||
* using the UIP_APPCALL definition. |
||||
* |
||||
* uIP applications can store the application state within the |
||||
* uip_conn structure by specifying the type of the application |
||||
* structure by typedef:ing the type uip_tcp_appstate_t and uip_udp_appstate_t. |
||||
* |
||||
* The file containing the definitions must be included in the |
||||
* uipopt.h file. |
||||
* |
||||
* The following example illustrates how this can look. |
||||
\code |
||||
|
||||
void httpd_appcall(void); |
||||
#define UIP_APPCALL httpd_appcall |
||||
|
||||
struct httpd_state { |
||||
u8_t state; |
||||
u16_t count; |
||||
char *dataptr; |
||||
char *script; |
||||
}; |
||||
typedef struct httpd_state uip_tcp_appstate_t |
||||
\endcode |
||||
*/ |
||||
|
||||
/**
|
||||
* \var #define UIP_APPCALL |
||||
* |
||||
* The name of the application function that uIP should call in |
||||
* response to TCP/IP events. |
||||
* |
||||
*/ |
||||
|
||||
/**
|
||||
* \var typedef uip_tcp_appstate_t |
||||
* |
||||
* The type of the application state that is to be stored in the |
||||
* uip_conn structure. This usually is typedef:ed to a struct holding |
||||
* application state information. |
||||
*/ |
||||
|
||||
/**
|
||||
* \var typedef uip_udp_appstate_t |
||||
* |
||||
* The type of the application state that is to be stored in the |
||||
* uip_conn structure. This usually is typedef:ed to a struct holding |
||||
* application state information. |
||||
*/ |
||||
/** @} */ |
||||
/** @} */ |
||||
|
||||
#endif /* __UIPOPT_H__ */ |
||||
Loading…
Reference in new issue