Latest compatible version of Classicube from the original GitHub repository (https://github.com/ClassiCube/ClassiCube) that can be compiled on Classicube for PowerMac PPC running Mac OS X 10.4.
This commit is contained in:
557
third_party/dsiwifi/arm_host/source/lwip/ipv4/acd.c
vendored
Normal file
557
third_party/dsiwifi/arm_host/source/lwip/ipv4/acd.c
vendored
Normal file
@@ -0,0 +1,557 @@
|
||||
/**
|
||||
* @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
|
||||
* Remvoe 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 */
|
||||
379
third_party/dsiwifi/arm_host/source/lwip/ipv4/autoip.c
vendored
Normal file
379
third_party/dsiwifi/arm_host/source/lwip/ipv4/autoip.c
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
/**
|
||||
* @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"));
|
||||
}
|
||||
|
||||
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"));
|
||||
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"));
|
||||
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()"));
|
||||
}
|
||||
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 */
|
||||
1976
third_party/dsiwifi/arm_host/source/lwip/ipv4/dhcp.c
vendored
Normal file
1976
third_party/dsiwifi/arm_host/source/lwip/ipv4/dhcp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1251
third_party/dsiwifi/arm_host/source/lwip/ipv4/etharp.c
vendored
Normal file
1251
third_party/dsiwifi/arm_host/source/lwip/ipv4/etharp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
405
third_party/dsiwifi/arm_host/source/lwip/ipv4/icmp.c
vendored
Normal file
405
third_party/dsiwifi/arm_host/source/lwip/ipv4/icmp.c
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* @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"));
|
||||
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"));
|
||||
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"));
|
||||
} else {
|
||||
err_t ret;
|
||||
struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
|
||||
ip4_addr_copy(iphdr->src, *src);
|
||||
ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
|
||||
ICMPH_TYPE_SET(iecho, ICMP_ER);
|
||||
#if CHECKSUM_GEN_ICMP
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
|
||||
/* adjust the checksum */
|
||||
if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
|
||||
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 */
|
||||
801
third_party/dsiwifi/arm_host/source/lwip/ipv4/igmp.c
vendored
Normal file
801
third_party/dsiwifi/arm_host/source/lwip/ipv4/igmp.c
vendored
Normal file
@@ -0,0 +1,801 @@
|
||||
/**
|
||||
* @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 */
|
||||
1166
third_party/dsiwifi/arm_host/source/lwip/ipv4/ip4.c
vendored
Normal file
1166
third_party/dsiwifi/arm_host/source/lwip/ipv4/ip4.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
323
third_party/dsiwifi/arm_host/source/lwip/ipv4/ip4_addr.c
vendored
Normal file
323
third_party/dsiwifi/arm_host/source/lwip/ipv4/ip4_addr.c
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* @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 */
|
||||
894
third_party/dsiwifi/arm_host/source/lwip/ipv4/ip4_frag.c
vendored
Normal file
894
third_party/dsiwifi/arm_host/source/lwip/ipv4/ip4_frag.c
vendored
Normal file
@@ -0,0 +1,894 @@
|
||||
/**
|
||||
* @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 */
|
||||
Reference in New Issue
Block a user