flycast/core/deps/picotcp/modules/pico_ipv4.c

1668 lines
45 KiB
C

/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera, Markian Yskout
*********************************************************************/
#include "pico_config.h"
#include "pico_ipfilter.h"
#include "pico_ipv4.h"
#include "pico_icmp4.h"
#include "pico_stack.h"
#include "pico_eth.h"
#include "pico_udp.h"
#include "pico_tcp.h"
#include "pico_socket.h"
#include "pico_device.h"
#include "pico_nat.h"
#include "pico_igmp.h"
#include "pico_tree.h"
#include "pico_aodv.h"
#include "pico_socket_multicast.h"
#include "pico_fragments.h"
#include "pico_ethernet.h"
#include "pico_mcast.h"
#ifdef PICO_SUPPORT_IPV4
#ifdef PICO_SUPPORT_MCAST
#ifdef DEBUG_MCAST
#define ip_mcast_dbg dbg
#else
#define ip_mcast_dbg(...) do {} while(0)
#endif
# define PICO_MCAST_ALL_HOSTS long_be(0xE0000001) /* 224.0.0.1 */
/* Default network interface for multicast transmission */
static struct pico_ipv4_link *mcast_default_link = NULL;
#endif
/* Queues */
static struct pico_queue in = {
0
};
static struct pico_queue out = {
0
};
/* Functions */
static int ipv4_route_compare(void *ka, void *kb);
static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size);
int pico_ipv4_compare(struct pico_ip4 *a, struct pico_ip4 *b)
{
if (a->addr < b->addr)
return -1;
if (a->addr > b->addr)
return 1;
return 0;
}
int pico_ipv4_to_string(char *ipbuf, const uint32_t ip)
{
const unsigned char *addr = (const unsigned char *) &ip;
int i;
if (!ipbuf) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
for(i = 0; i < 4; i++)
{
if (addr[i] > 99) {
*ipbuf++ = (char)('0' + (addr[i] / 100));
*ipbuf++ = (char)('0' + ((addr[i] % 100) / 10));
*ipbuf++ = (char)('0' + ((addr[i] % 100) % 10));
} else if (addr[i] > 9) {
*ipbuf++ = (char)('0' + (addr[i] / 10));
*ipbuf++ = (char)('0' + (addr[i] % 10));
} else {
*ipbuf++ = (char)('0' + addr[i]);
}
if (i < 3)
*ipbuf++ = '.';
}
*ipbuf = '\0';
return 0;
}
static int pico_string_check_null_args(const char *ipstr, uint32_t *ip)
{
if (!ipstr || !ip) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
return 0;
}
int pico_string_to_ipv4(const char *ipstr, uint32_t *ip)
{
unsigned char buf[PICO_SIZE_IP4] = {
0
};
int cnt = 0;
char p;
if (pico_string_check_null_args(ipstr, ip) < 0)
return -1;
while((p = *ipstr++) != 0 && cnt < PICO_SIZE_IP4)
{
if (pico_is_digit(p)) {
buf[cnt] = (uint8_t)((10 * buf[cnt]) + (p - '0'));
} else if (p == '.') {
cnt++;
} else {
return -1;
}
}
/* Handle short notation */
if (cnt == 1) {
buf[3] = buf[1];
buf[1] = 0;
buf[2] = 0;
} else if (cnt == 2) {
buf[3] = buf[2];
buf[2] = 0;
} else if (cnt != 3) {
/* String could not be parsed, return error */
return -1;
}
*ip = long_from(buf);
return 0;
}
int pico_ipv4_valid_netmask(uint32_t mask)
{
int cnt = 0;
int end = 0;
int i;
uint32_t mask_swap = long_be(mask);
/*
* Swap bytes for convenient parsing
* e.g. 0x..f8ff will become 0xfff8..
* Then, we count the consecutive bits
*
* */
for(i = 0; i < 32; i++) {
if ((mask_swap << i) & 0x80000000) {
if (end) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
cnt++;
} else {
end = 1;
}
}
return cnt;
}
int pico_ipv4_is_unicast(uint32_t address)
{
const unsigned char *addr = (unsigned char *) &address;
if ((addr[0] & 0xe0) == 0xe0)
return 0; /* multicast */
return 1;
}
int pico_ipv4_is_multicast(uint32_t address)
{
const unsigned char *addr = (unsigned char *) &address;
if ((addr[0] != 0xff) && ((addr[0] & 0xe0) == 0xe0))
return 1; /* multicast */
return 0;
}
int pico_ipv4_is_loopback(uint32_t address)
{
const unsigned char *addr = (unsigned char *) &address;
if (addr[0] == 0x7f)
return 1;
return 0;
}
static int pico_ipv4_is_invalid_loopback(uint32_t address, struct pico_device *dev)
{
return pico_ipv4_is_loopback(address) && ((!dev) || strcmp(dev->name, "loop"));
}
int pico_ipv4_is_valid_src(uint32_t address, struct pico_device *dev)
{
if (pico_ipv4_is_broadcast(address)) {
dbg("Source is a broadcast address, discard packet %08x\n", address);
return 0;
} else if ( pico_ipv4_is_multicast(address)) {
dbg("Source is a multicast address, discard packet\n");
return 0;
} else if (pico_ipv4_is_invalid_loopback(address, dev)) {
dbg("Source is a loopback address, discard packet\n");
return 0;
} else {
#ifdef PICO_SUPPORT_AODV
union pico_address src;
src.ip4.addr = address;
pico_aodv_refresh(&src);
#endif
return 1;
}
}
static int pico_ipv4_checksum(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
if (!hdr)
return -1;
hdr->crc = 0;
hdr->crc = short_be(pico_checksum(hdr, f->net_len));
return 0;
}
#ifdef PICO_SUPPORT_CRC
static inline int pico_ipv4_crc_check(struct pico_frame *f)
{
uint16_t checksum_invalid = 1;
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
checksum_invalid = short_be(pico_checksum(hdr, f->net_len));
if (checksum_invalid) {
dbg("IP: checksum failed!\n");
pico_frame_discard(f);
return 0;
}
return 1;
}
#else
static inline int pico_ipv4_crc_check(struct pico_frame *f)
{
IGNORE_PARAMETER(f);
return 1;
}
#endif /* PICO_SUPPORT_CRC */
static int pico_ipv4_forward(struct pico_frame *f);
#ifdef PICO_SUPPORT_MCAST
static int pico_ipv4_mcast_filter(struct pico_frame *f);
#endif
static int ipv4_link_compare(void *ka, void *kb)
{
struct pico_ipv4_link *a = ka, *b = kb;
int cmp = pico_ipv4_compare(&a->address, &b->address);
if (cmp)
return cmp;
/* zero can be assigned multiple times (e.g. for DHCP) */
if (a->dev != NULL && b->dev != NULL && a->address.addr == PICO_IP4_ANY && b->address.addr == PICO_IP4_ANY) {
if (a->dev < b->dev)
return -1;
if (a->dev > b->dev)
return 1;
}
return 0;
}
static PICO_TREE_DECLARE(Tree_dev_link, ipv4_link_compare);
static int pico_ipv4_process_bcast_in(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
#ifdef PICO_SUPPORT_UDP
if (pico_ipv4_is_broadcast(hdr->dst.addr) && (hdr->proto == PICO_PROTO_UDP)) {
/* Receiving UDP broadcast datagram */
f->flags |= PICO_FRAME_FLAG_BCAST;
pico_enqueue(pico_proto_udp.q_in, f);
return 1;
}
#endif
#ifdef PICO_SUPPORT_ICMP4
if (pico_ipv4_is_broadcast(hdr->dst.addr) && (hdr->proto == PICO_PROTO_ICMP4)) {
/* Receiving ICMP4 bcast packet */
f->flags |= PICO_FRAME_FLAG_BCAST;
pico_enqueue(pico_proto_icmp4.q_in, f);
return 1;
}
#endif
return 0;
}
static int pico_ipv4_process_mcast_in(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
if (pico_ipv4_is_multicast(hdr->dst.addr)) {
#ifdef PICO_SUPPORT_IGMP
/* Receiving UDP multicast datagram TODO set f->flags? */
if (hdr->proto == PICO_PROTO_IGMP) {
ip_mcast_dbg("MCAST: received IGMP message\n");
pico_transport_receive(f, PICO_PROTO_IGMP);
return 1;
} else if ((pico_ipv4_mcast_filter(f) == 0) && (hdr->proto == PICO_PROTO_UDP)) {
pico_enqueue(pico_proto_udp.q_in, f);
return 1;
}
#endif
pico_frame_discard(f);
return 1;
}
return 0;
}
static int pico_ipv4_process_local_unicast_in(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
struct pico_ipv4_link test = {
.address = {.addr = PICO_IP4_ANY}, .dev = NULL
};
if (pico_ipv4_link_find(&hdr->dst)) {
if (pico_ipv4_nat_inbound(f, &hdr->dst) == 0)
pico_enqueue(pico_proto_ipv4.q_in, f); /* dst changed, reprocess */
else
pico_transport_receive(f, hdr->proto);
return 1;
} else if (f->dev->proxied) {
// Proxied device: deliver all traffic locally
pico_transport_receive(f, hdr->proto);
return 1;
} else if (pico_tree_findKey(&Tree_dev_link, &test)) {
#ifdef PICO_SUPPORT_UDP
/* address of this device is apparently 0.0.0.0; might be a DHCP packet */
/* XXX KRO: is obsolete. Broadcast flag is set on outgoing DHCP messages.
* incomming DHCP messages are to be broadcasted. Our current DHCP server
* implementation does not take this flag into account yet though ... */
pico_enqueue(pico_proto_udp.q_in, f);
return 1;
#endif
}
return 0;
}
static void pico_ipv4_process_finally_try_forward(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
if ((pico_ipv4_is_broadcast(hdr->dst.addr)) || ((f->flags & PICO_FRAME_FLAG_BCAST) != 0)) {
/* don't forward broadcast frame, discard! */
pico_frame_discard(f);
} else if (pico_ipv4_forward(f) != 0) {
pico_frame_discard(f);
/* dbg("Forward failed.\n"); */
}
}
static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f)
{
uint8_t option_len = 0;
int ret = 0;
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
uint16_t max_allowed = (uint16_t) ((int)f->buffer_len - (f->net_hdr - f->buffer) - (int)PICO_SIZE_IP4HDR);
if (!hdr)
return -1;
(void)self;
/* NAT needs transport header information */
if (((hdr->vhl) & 0x0F) > 5) {
option_len = (uint8_t)(4 * (((hdr->vhl) & 0x0F) - 5));
}
f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR + option_len;
f->transport_len = (uint16_t)(short_be(hdr->len) - PICO_SIZE_IP4HDR - option_len);
f->net_len = (uint16_t)(PICO_SIZE_IP4HDR + option_len);
#if defined(PICO_SUPPORT_IPV4FRAG) || defined(PICO_SUPPORT_IPV6FRAG)
f->frag = short_be(hdr->frag);
#endif
if (f->transport_len > max_allowed) {
pico_frame_discard(f);
return 0; /* Packet is discarded due to unfeasible length */
}
#ifdef PICO_SUPPORT_IPFILTER
if (ipfilter(f)) {
/*pico_frame is discarded as result of the filtering*/
return 0;
}
#endif
/* ret == 1 indicates to continue the function */
ret = pico_ipv4_crc_check(f);
if (ret < 1)
return ret;
/* Validate source IP address. Discard quietly if invalid */
if (!pico_ipv4_is_valid_src(hdr->src.addr, f->dev)) {
pico_frame_discard(f);
return 0;
}
#if defined(PICO_SUPPORT_IPV4FRAG) || defined(PICO_SUPPORT_IPV6FRAG)
if (f->frag & PICO_IPV4_EVIL) {
(void)pico_icmp4_param_problem(f, 0);
pico_frame_discard(f); /* RFC 3514 */
return 0;
}
#endif
if ((hdr->vhl & 0x0f) < 5) {
/* RFC 791: IHL minimum value is 5 */
(void)pico_icmp4_param_problem(f, 0);
pico_frame_discard(f);
return 0;
}
#if defined(PICO_SUPPORT_IPV4FRAG) || defined(PICO_SUPPORT_IPV6FRAG)
if (f->frag & (PICO_IPV4_MOREFRAG | PICO_IPV4_FRAG_MASK))
{
#ifdef PICO_SUPPORT_IPV4FRAG
pico_ipv4_process_frag(hdr, f, hdr->proto);
/* Frame can be discarded, frag will handle its own copy */
#endif
/* We do not support fragmentation, discard quietly */
pico_frame_discard(f);
return 0;
}
#endif
if (pico_ipv4_process_bcast_in(f) > 0)
return 0;
if (pico_ipv4_process_mcast_in(f) > 0)
return 0;
if (pico_ipv4_process_local_unicast_in(f) > 0)
return 0;
pico_ipv4_process_finally_try_forward(f);
return 0;
}
PICO_TREE_DECLARE(Routes, ipv4_route_compare);
static int pico_ipv4_process_out(struct pico_protocol *self, struct pico_frame *f)
{
IGNORE_PARAMETER(self);
f->start = (uint8_t*) f->net_hdr;
#ifdef PICO_SUPPORT_IPFILTER
if (ipfilter(f)) {
/*pico_frame is discarded as result of the filtering*/
return 0;
}
#endif
return pico_datalink_send(f);
}
static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size)
{
struct pico_frame *f = NULL;
IGNORE_PARAMETER(self);
f = pico_proto_ethernet.alloc(&pico_proto_ethernet, dev, (uint16_t)(size + PICO_SIZE_IP4HDR));
/* TODO: In 6LoWPAN topic branch update to make use of dev->ll_mode */
if (!f)
return NULL;
f->net_len = PICO_SIZE_IP4HDR;
f->transport_hdr = f->net_hdr + PICO_SIZE_IP4HDR;
f->transport_len = (uint16_t)size;
/* Datalink size is accounted for in pico_datalink_send (link layer) */
f->len = (uint32_t)(size + PICO_SIZE_IP4HDR);
return f;
}
static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f);
/* Interface: protocol definition */
struct pico_protocol pico_proto_ipv4 = {
.name = "ipv4",
.proto_number = PICO_PROTO_IPV4,
.layer = PICO_LAYER_NETWORK,
.alloc = pico_ipv4_alloc,
.process_in = pico_ipv4_process_in,
.process_out = pico_ipv4_process_out,
.push = pico_ipv4_frame_sock_push,
.q_in = &in,
.q_out = &out,
};
static int ipv4_route_compare(void *ka, void *kb)
{
struct pico_ipv4_route *a = ka, *b = kb;
uint32_t a_nm, b_nm;
int cmp;
a_nm = long_be(a->netmask.addr);
b_nm = long_be(b->netmask.addr);
/* Routes are sorted by (host side) netmask len, then by addr, then by metric. */
if (a_nm < b_nm)
return -1;
if (b_nm < a_nm)
return 1;
cmp = pico_ipv4_compare(&a->dest, &b->dest);
if (cmp)
return cmp;
if (a->metric < b->metric)
return -1;
if (a->metric > b->metric)
return 1;
return 0;
}
static struct pico_ipv4_route default_bcast_route = {
.dest = {PICO_IP4_BCAST},
.netmask = {PICO_IP4_BCAST},
.gateway = { 0 },
.link = NULL,
.metric = 1000
};
static struct pico_ipv4_route *route_find_default_bcast(void)
{
return &default_bcast_route;
}
static struct pico_ipv4_route *route_find(const struct pico_ip4 *addr)
{
struct pico_ipv4_route *r;
struct pico_tree_node *index;
if (addr->addr == PICO_IP4_ANY) {
return NULL;
}
if (addr->addr != PICO_IP4_BCAST) {
pico_tree_foreach_reverse(index, &Routes) {
r = index->keyValue;
if ((addr->addr & (r->netmask.addr)) == (r->dest.addr)) {
return r;
}
}
return NULL;
}
return route_find_default_bcast();
}
struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr)
{
struct pico_ip4 nullip;
struct pico_ipv4_route *route;
nullip.addr = 0U;
if (!addr) {
pico_err = PICO_ERR_EINVAL;
return nullip;
}
route = route_find(addr);
if (!route) {
pico_err = PICO_ERR_EHOSTUNREACH;
return nullip;
}
else
return route->gateway;
}
struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst)
{
struct pico_ip4 *myself = NULL;
struct pico_ipv4_route *rt;
#ifdef PICO_SUPPORT_AODV
union pico_address node_address;
#endif
if (!dst) {
pico_err = PICO_ERR_EINVAL;
return NULL;
}
#ifdef PICO_SUPPORT_AODV
node_address.ip4.addr = dst->addr;
if (dst->addr && pico_ipv4_is_unicast(dst->addr))
pico_aodv_lookup(&node_address);
#endif
rt = route_find(dst);
if (rt && rt->link) {
myself = &rt->link->address;
} else {
pico_err = PICO_ERR_EHOSTUNREACH;
}
return myself;
}
struct pico_device *pico_ipv4_source_dev_find(const struct pico_ip4 *dst)
{
struct pico_device *dev = NULL;
struct pico_ipv4_route *rt;
if (!dst) {
pico_err = PICO_ERR_EINVAL;
return NULL;
}
rt = route_find(dst);
if (rt && rt->link) {
dev = rt->link->dev;
} else {
pico_err = PICO_ERR_EHOSTUNREACH;
}
return dev;
}
#ifdef PICO_SUPPORT_MCAST
/* link
* |
* MCASTGroups
* | | |
* ------------ | ------------
* | | |
* MCASTSources MCASTSources MCASTSources
* | | | | | | | | | | | |
* S S S S S S S S S S S S
*
* MCASTGroups: RBTree(mcast_group)
* MCASTSources: RBTree(source)
*/
static int ipv4_mcast_groups_cmp(void *ka, void *kb)
{
struct pico_mcast_group *a = ka, *b = kb;
return pico_ipv4_compare(&a->mcast_addr.ip4, &b->mcast_addr.ip4);
}
static int ipv4_mcast_sources_cmp(void *ka, void *kb)
{
struct pico_ip4 *a = ka, *b = kb;
return pico_ipv4_compare(a, b);
}
static void pico_ipv4_mcast_print_groups(struct pico_ipv4_link *mcast_link)
{
uint16_t i = 0;
struct pico_mcast_group *g = NULL;
struct pico_ip4 *source = NULL;
struct pico_tree_node *index = NULL, *index2 = NULL;
(void) source;
ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
ip_mcast_dbg("+ MULTICAST list interface %-16s +\n", mcast_link->dev->name);
ip_mcast_dbg("+---------------------------------------------------------------------------------+\n");
ip_mcast_dbg("+ nr | interface | host group | reference count | filter mode | source +\n");
ip_mcast_dbg("+---------------------------------------------------------------------------------+\n");
pico_tree_foreach(index, mcast_link->MCASTGroups) {
g = index->keyValue;
ip_mcast_dbg("+ %04d | %16s | %08X | %05u | %u | %8s +\n", i, mcast_link->dev->name, g->mcast_addr.ip4.addr, g->reference_count, g->filter_mode, "");
pico_tree_foreach(index2, &g->MCASTSources) {
source = index2->keyValue;
ip_mcast_dbg("+ %4s | %16s | %8s | %5s | %s | %08X +\n", "", "", "", "", "", source->addr);
}
i++;
}
ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
}
static int mcast_group_update(struct pico_mcast_group *g, struct pico_tree *MCASTFilter, uint8_t filter_mode)
{
struct pico_tree_node *index = NULL, *_tmp = NULL;
struct pico_ip4 *source = NULL;
/* cleanup filter */
pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) {
source = index->keyValue;
pico_tree_delete(&g->MCASTSources, source);
PICO_FREE(source);
}
/* insert new filter */
if (MCASTFilter) {
pico_tree_foreach(index, MCASTFilter) {
if (index->keyValue) {
source = PICO_ZALLOC(sizeof(struct pico_ip4));
if (!source) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
source->addr = ((struct pico_ip4 *)index->keyValue)->addr;
if (pico_tree_insert(&g->MCASTSources, source)) {
dbg("IPv4: Failed to insert source in tree\n");
PICO_FREE(source);
return -1;
}
}
}
}
g->filter_mode = filter_mode;
return 0;
}
int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
{
struct pico_mcast_group *g = NULL, test = {
0
};
struct pico_ipv4_link *link = NULL;
if (mcast_link)
link = pico_ipv4_link_get(mcast_link);
if (!link)
link = mcast_default_link;
test.mcast_addr.ip4 = *mcast_group;
g = pico_tree_findKey(link->MCASTGroups, &test);
if (g) {
if (reference_count)
g->reference_count++;
#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
#endif
} else {
g = PICO_ZALLOC(sizeof(struct pico_mcast_group));
if (!g) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
/* "non-existent" state of filter mode INCLUDE and empty source list */
g->filter_mode = PICO_IP_MULTICAST_INCLUDE;
g->reference_count = 1;
g->mcast_addr.ip4 = *mcast_group;
g->MCASTSources.root = &LEAF;
g->MCASTSources.compare = ipv4_mcast_sources_cmp;
if (pico_tree_insert(link->MCASTGroups, g)) {
dbg("IPv4: Failed to insert group in tree\n");
PICO_FREE(g);
return -1;
}
#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_CREATE);
#endif
}
if (mcast_group_update(g, MCASTFilter, filter_mode) < 0) {
dbg("Error in mcast_group update\n");
return -1;
}
pico_ipv4_mcast_print_groups(link);
return 0;
}
int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
{
struct pico_mcast_group *g = NULL, test = {
0
};
struct pico_ipv4_link *link = NULL;
struct pico_tree_node *index = NULL, *_tmp = NULL;
struct pico_ip4 *source = NULL;
if (mcast_link)
link = pico_ipv4_link_get(mcast_link);
if (!link)
link = mcast_default_link;
if (!link)
return -1;
test.mcast_addr.ip4 = *mcast_group;
g = pico_tree_findKey(link->MCASTGroups, &test);
if (!g) {
pico_err = PICO_ERR_EINVAL;
return -1;
} else {
if (reference_count && (--(g->reference_count) < 1)) {
#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_DELETE);
#endif
/* cleanup filter */
pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) {
source = index->keyValue;
pico_tree_delete(&g->MCASTSources, source);
PICO_FREE(source);
}
pico_tree_delete(link->MCASTGroups, g);
PICO_FREE(g);
} else {
#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
#endif
if (mcast_group_update(g, MCASTFilter, filter_mode) < 0)
return -1;
}
}
pico_ipv4_mcast_print_groups(link);
return 0;
}
struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void)
{
return mcast_default_link;
}
static int pico_ipv4_mcast_filter(struct pico_frame *f)
{
struct pico_ipv4_link *link = NULL;
struct pico_tree_node *index = NULL, *index2 = NULL;
struct pico_mcast_group *g = NULL, test = {
0
};
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
test.mcast_addr.ip4 = hdr->dst;
pico_tree_foreach(index, &Tree_dev_link) {
link = index->keyValue;
g = pico_tree_findKey(link->MCASTGroups, &test);
if (g) {
if (f->dev == link->dev) {
ip_mcast_dbg("MCAST: IP %08X is group member of current link %s\n", hdr->dst.addr, f->dev->name);
/* perform source filtering */
switch (g->filter_mode) {
case PICO_IP_MULTICAST_INCLUDE:
pico_tree_foreach(index2, &g->MCASTSources) {
if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) {
ip_mcast_dbg("MCAST: IP %08X in included interface source list\n", hdr->src.addr);
return 0;
}
}
ip_mcast_dbg("MCAST: IP %08X NOT in included interface source list\n", hdr->src.addr);
return -1;
case PICO_IP_MULTICAST_EXCLUDE:
pico_tree_foreach(index2, &g->MCASTSources) {
if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) {
ip_mcast_dbg("MCAST: IP %08X in excluded interface source list\n", hdr->src.addr);
return -1;
}
}
ip_mcast_dbg("MCAST: IP %08X NOT in excluded interface source list\n", hdr->src.addr);
return 0;
default:
return -1;
}
} else {
ip_mcast_dbg("MCAST: IP %08X is group member of different link %s\n", hdr->dst.addr, link->dev->name);
}
} else {
ip_mcast_dbg("MCAST: IP %08X is not a group member of link %s\n", hdr->dst.addr, f->dev->name);
}
}
return -1;
}
#else
int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
{
IGNORE_PARAMETER(mcast_link);
IGNORE_PARAMETER(mcast_group);
IGNORE_PARAMETER(reference_count);
IGNORE_PARAMETER(filter_mode);
IGNORE_PARAMETER(MCASTFilter);
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
{
IGNORE_PARAMETER(mcast_link);
IGNORE_PARAMETER(mcast_group);
IGNORE_PARAMETER(reference_count);
IGNORE_PARAMETER(filter_mode);
IGNORE_PARAMETER(MCASTFilter);
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void)
{
pico_err = PICO_ERR_EPROTONOSUPPORT;
return NULL;
}
#endif /* PICO_SUPPORT_MCAST */
/* #define DEBUG_ROUTE */
#ifdef DEBUG_ROUTE
void dbg_route(void)
{
struct pico_ipv4_route *r;
struct pico_tree_node *index;
int count_hosts = 0;
dbg("==== ROUTING TABLE =====\n");
pico_tree_foreach(index, &Routes) {
r = index->keyValue;
dbg("Route to %08x/%08x, gw %08x, dev: %s, metric: %d\n", r->dest.addr, r->netmask.addr, r->gateway.addr, r->link->dev->name, r->metric);
if (r->netmask.addr == 0xFFFFFFFF)
count_hosts++;
}
dbg("================ total HOST nodes: %d ======\n\n\n", count_hosts);
}
#else
#define dbg_route() do { } while(0)
#endif
int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto)
{
struct pico_ipv4_route *route;
struct pico_ipv4_link *link;
struct pico_ipv4_hdr *hdr;
uint8_t ttl = PICO_IPV4_DEFAULT_TTL;
uint8_t vhl = 0x45; /* version 4, header length 20 */
int32_t retval = 0;
static uint16_t ipv4_progressive_id = 0x91c0;
#ifdef PICO_SUPPORT_MCAST
struct pico_tree_node *index;
#endif
if (!f || !dst) {
pico_err = PICO_ERR_EINVAL;
goto drop;
}
hdr = (struct pico_ipv4_hdr *) f->net_hdr;
if (!hdr) {
dbg("IP header error\n");
pico_err = PICO_ERR_EINVAL;
goto drop;
}
if (dst->addr == 0) {
dbg("IP destination addr error\n");
pico_err = PICO_ERR_EINVAL;
goto drop;
}
route = route_find(dst);
if (!route) {
/* dbg("Route to %08x not found.\n", long_be(dst->addr)); */
pico_err = PICO_ERR_EHOSTUNREACH;
goto drop;
} else {
link = route->link;
#ifdef PICO_SUPPORT_MCAST
if (pico_ipv4_is_multicast(dst->addr)) { /* if multicast */
switch (proto) {
case PICO_PROTO_UDP:
if (pico_udp_get_mc_ttl(f->sock, &ttl) < 0)
ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
break;
#ifdef PICO_SUPPORT_IGMP
case PICO_PROTO_IGMP:
vhl = 0x46; /* header length 24 */
ttl = 1;
/* router alert (RFC 2113) */
hdr->options[0] = 0x94;
hdr->options[1] = 0x04;
hdr->options[2] = 0x00;
hdr->options[3] = 0x00;
if (f->dev && link->dev != f->dev) { /* default link is not requested link */
pico_tree_foreach(index, &Tree_dev_link) {
link = index->keyValue;
if (link->dev == f->dev)
break;
}
}
break;
#endif
default:
ttl = PICO_IPV4_DEFAULT_TTL;
}
}
#endif
}
hdr->vhl = vhl;
hdr->len = short_be((uint16_t)(f->transport_len + f->net_len));
hdr->id = short_be(ipv4_progressive_id);
if (
#ifdef PICO_SUPPORT_IPV4FRAG
(0 == (f->frag & PICO_IPV4_MOREFRAG)) &&
#endif
1 )
ipv4_progressive_id++;
if (f->send_ttl > 0) {
ttl = f->send_ttl;
}
hdr->dst.addr = dst->addr;
if (f->local_ip.addr)
hdr->src.addr = f->local_ip.addr; // Masqueraded
else
hdr->src.addr = link->address.addr;
hdr->ttl = ttl;
hdr->tos = f->send_tos;
hdr->proto = proto;
hdr->frag = short_be(PICO_IPV4_DONTFRAG);
#ifdef PICO_SUPPORT_IPV4FRAG
# ifdef PICO_SUPPORT_UDP
if (proto == PICO_PROTO_UDP) {
/* first fragment, can not use transport_len to calculate IP length */
if (f->transport_hdr != f->payload)
hdr->len = short_be((uint16_t)(f->payload_len + sizeof(struct pico_udp_hdr) + f->net_len));
/* set fragmentation flags and offset calculated in socket layer */
hdr->frag = short_be(f->frag);
}
if (proto == PICO_PROTO_ICMP4)
{
hdr->frag = short_be(f->frag);
}
# endif
#endif /* PICO_SUPPORT_IPV4FRAG */
pico_ipv4_checksum(f);
if (f->sock && f->sock->dev) {
/* if the socket has its device set, use that (currently used for DHCP) */
f->dev = f->sock->dev;
} else {
f->dev = link->dev;
if (f->sock)
f->sock->dev = f->dev;
}
#ifdef PICO_SUPPORT_MCAST
if (pico_ipv4_is_multicast(hdr->dst.addr)) {
struct pico_frame *cpy;
/* Sending UDP multicast datagram, am I member? If so, loopback copy */
if ((proto != PICO_PROTO_IGMP) && (pico_ipv4_mcast_filter(f) == 0)) {
ip_mcast_dbg("MCAST: sender is member of group, loopback copy\n");
cpy = pico_frame_copy(f);
if (!cpy) {
pico_err = PICO_ERR_ENOMEM;
ip_mcast_dbg("MCAST: Failed to copy frame\n");
goto drop;
}
retval = pico_enqueue(&in, cpy);
if (retval <= 0)
pico_frame_discard(cpy);
}
}
#endif
/* #ifdef PICO_SUPPORT_AODV */
#if 0
{
union pico_address node_address;
node_address.ip4.addr = hdr->dst.addr;
if(hdr->dst.addr && pico_ipv4_is_unicast(hdr->dst.addr))
pico_aodv_lookup(&node_address);
}
#endif
if (pico_ipv4_link_get(&hdr->dst)) {
/* it's our own IP */
retval = pico_enqueue(&in, f);
if (retval > 0)
return retval;
} else{
/* TODO: Check if there are members subscribed here */
retval = pico_enqueue(&out, f);
if (retval > 0)
return retval;
}
drop:
pico_frame_discard(f);
return -1;
}
static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f)
{
struct pico_ip4 *dst;
struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *) f->info;
IGNORE_PARAMETER(self);
if (!f->sock) {
pico_frame_discard(f);
return -1;
}
if (remote_endpoint) {
dst = &remote_endpoint->remote_addr.ip4;
} else {
dst = &f->sock->remote_addr.ip4;
}
return pico_ipv4_frame_push(f, dst, (uint8_t)f->sock->proto->proto_number);
}
int MOCKABLE pico_ipv4_route_add(struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link)
{
struct pico_ipv4_route test, *new;
test.dest.addr = address.addr;
test.netmask.addr = netmask.addr;
test.metric = (uint32_t)metric;
if (pico_tree_findKey(&Routes, &test)) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
new = PICO_ZALLOC(sizeof(struct pico_ipv4_route));
if (!new) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
new->dest.addr = address.addr;
new->netmask.addr = netmask.addr;
new->gateway.addr = gateway.addr;
new->metric = (uint32_t)metric;
if (gateway.addr == 0) {
/* No gateway provided, use the link */
new->link = link;
} else {
struct pico_ipv4_route *r = route_find(&gateway);
if (!r ) { /* Specified Gateway is unreachable */
pico_err = PICO_ERR_EHOSTUNREACH;
PICO_FREE(new);
return -1;
}
if (r->gateway.addr) { /* Specified Gateway is not a neighbor */
pico_err = PICO_ERR_ENETUNREACH;
PICO_FREE(new);
return -1;
}
new->link = r->link;
}
if (!new->link) {
pico_err = PICO_ERR_EINVAL;
PICO_FREE(new);
return -1;
}
if (pico_tree_insert(&Routes, new)) {
dbg("IPv4: Failed to insert route in tree\n");
PICO_FREE(new);
return -1;
}
dbg_route();
return 0;
}
int pico_ipv4_route_del(struct pico_ip4 address, struct pico_ip4 netmask, int metric)
{
struct pico_ipv4_route test, *found;
test.dest.addr = address.addr;
test.netmask.addr = netmask.addr;
test.metric = (uint32_t)metric;
found = pico_tree_findKey(&Routes, &test);
if (found) {
pico_tree_delete(&Routes, found);
PICO_FREE(found);
dbg_route();
return 0;
}
pico_err = PICO_ERR_EINVAL;
return -1;
}
int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask)
{
struct pico_ipv4_link test, *new;
struct pico_ip4 network, gateway;
char ipstr[30];
if (!dev) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
test.address.addr = address.addr;
test.netmask.addr = netmask.addr;
test.dev = dev;
/** XXX: Valid netmask / unicast address test **/
if (pico_tree_findKey(&Tree_dev_link, &test)) {
pico_err = PICO_ERR_EADDRINUSE;
return -1;
}
/** XXX: Check for network already in use (e.g. trying to assign 10.0.0.1/24 where 10.1.0.1/8 is in use) **/
new = PICO_ZALLOC(sizeof(struct pico_ipv4_link));
if (!new) {
dbg("IPv4: Out of memory!\n");
pico_err = PICO_ERR_ENOMEM;
return -1;
}
new->address.addr = address.addr;
new->netmask.addr = netmask.addr;
new->dev = dev;
#ifdef PICO_SUPPORT_MCAST
new->MCASTGroups = PICO_ZALLOC(sizeof(struct pico_tree));
if (!new->MCASTGroups) {
PICO_FREE(new);
dbg("IPv4: Out of memory!\n");
pico_err = PICO_ERR_ENOMEM;
return -1;
}
new->MCASTGroups->root = &LEAF;
new->MCASTGroups->compare = ipv4_mcast_groups_cmp;
#ifdef PICO_SUPPORT_IGMP
new->mcast_compatibility = PICO_IGMPV3; /* default RFC 3376 $7.2.1 */
new->mcast_last_query_interval = PICO_IGMP_QUERY_INTERVAL;
#endif
#endif
if (pico_tree_insert(&Tree_dev_link, new)) {
dbg("IPv4: Failed to insert link in tree\n");
#ifdef PICO_SUPPORT_MCAST
PICO_FREE(new->MCASTGroups);
#endif
PICO_FREE(new);
return -1;
}
#ifdef PICO_SUPPORT_MCAST
do {
struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm, mcast_gw;
if (!mcast_default_link) {
mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */
mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */
mcast_gw.addr = long_be(0x00000000);
mcast_default_link = new;
pico_ipv4_route_add(mcast_addr, mcast_nm, mcast_gw, 1, new);
}
mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS;
pico_ipv4_mcast_join(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL);
} while(0);
#endif
network.addr = address.addr & netmask.addr;
gateway.addr = 0U;
pico_ipv4_route_add(network, netmask, gateway, 1, new);
pico_ipv4_to_string(ipstr, new->address.addr);
dbg("Assigned ipv4 %s to device %s\n", ipstr, new->dev->name);
if (default_bcast_route.link == NULL)
default_bcast_route.link = new;
return 0;
}
static int pico_ipv4_cleanup_routes(struct pico_ipv4_link *link)
{
struct pico_tree_node *index = NULL, *tmp = NULL;
struct pico_ipv4_route *route = NULL;
pico_tree_foreach_safe(index, &Routes, tmp) {
route = index->keyValue;
if (link == route->link)
pico_ipv4_route_del(route->dest, route->netmask, (int)route->metric);
}
return 0;
}
void MOCKABLE pico_ipv4_route_set_bcast_link(struct pico_ipv4_link *link)
{
if (link)
default_bcast_route.link = link;
}
int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address)
{
struct pico_ipv4_link test, *found;
if (!dev) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
test.address.addr = address.addr;
test.dev = dev;
found = pico_tree_findKey(&Tree_dev_link, &test);
if (!found) {
pico_err = PICO_ERR_ENXIO;
return -1;
}
#ifdef PICO_SUPPORT_MCAST
do {
struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm;
struct pico_mcast_group *g = NULL;
struct pico_tree_node *index, *_tmp;
if (found == mcast_default_link) {
mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */
mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */
mcast_default_link = NULL;
pico_ipv4_route_del(mcast_addr, mcast_nm, 1);
}
mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS;
pico_ipv4_mcast_leave(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL);
pico_tree_foreach_safe(index, found->MCASTGroups, _tmp) {
g = index->keyValue;
pico_tree_delete(found->MCASTGroups, g);
PICO_FREE(g);
}
} while(0);
PICO_FREE(found->MCASTGroups);
#endif
pico_ipv4_cleanup_routes(found);
pico_tree_delete(&Tree_dev_link, found);
if (default_bcast_route.link == found)
default_bcast_route.link = NULL;
PICO_FREE(found);
return 0;
}
struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address)
{
struct pico_ipv4_link test = {
0
}, *found = NULL;
test.address.addr = address->addr;
found = pico_tree_findKey(&Tree_dev_link, &test);
if (!found)
return NULL;
else
return found;
}
struct pico_ipv4_link *MOCKABLE pico_ipv4_link_by_dev(struct pico_device *dev)
{
struct pico_tree_node *index = NULL;
struct pico_ipv4_link *link = NULL;
pico_tree_foreach(index, &Tree_dev_link) {
link = index->keyValue;
if (link->dev == dev)
return link;
}
return NULL;
}
struct pico_ipv4_link *pico_ipv4_link_by_dev_next(struct pico_device *dev, struct pico_ipv4_link *last)
{
struct pico_tree_node *index = NULL;
struct pico_ipv4_link *link = NULL;
int valid = 0;
if (last == NULL)
valid = 1;
pico_tree_foreach(index, &Tree_dev_link) {
link = index->keyValue;
if (link->dev == dev) {
if (last == link)
valid = 1;
else if (valid > 0)
return link;
}
}
return NULL;
}
struct pico_device *MOCKABLE pico_ipv4_link_find(struct pico_ip4 *address)
{
struct pico_ipv4_link test, *found;
if (!address) {
pico_err = PICO_ERR_EINVAL;
return NULL;
}
test.dev = NULL;
test.address.addr = address->addr;
found = pico_tree_findKey(&Tree_dev_link, &test);
if (!found) {
pico_err = PICO_ERR_ENXIO;
return NULL;
}
return found->dev;
}
static int pico_ipv4_rebound_large(struct pico_frame *f)
{
#ifdef PICO_SUPPORT_IPV4FRAG
uint16_t total_payload_written = 0;
uint32_t len = f->transport_len;
struct pico_frame *fr;
struct pico_ip4 dst;
struct pico_ipv4_hdr *hdr;
hdr = (struct pico_ipv4_hdr *) f->net_hdr;
dst.addr = hdr->src.addr;
while(total_payload_written < len) {
uint32_t space = (uint32_t)len - total_payload_written;
if (space > PICO_IPV4_MAXPAYLOAD)
space = PICO_IPV4_MAXPAYLOAD;
fr = pico_ipv4_alloc(&pico_proto_ipv4, NULL, (uint16_t)space);
if (!fr) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
if (space + total_payload_written < len)
{
fr->frag |= PICO_IPV4_MOREFRAG;
}
else
{
fr->frag &= PICO_IPV4_FRAG_MASK;
}
fr->frag = (((total_payload_written) >> 3u) & 0xffffu) | fr->frag;
memcpy(fr->transport_hdr, f->transport_hdr + total_payload_written, fr->transport_len);
if (pico_ipv4_frame_push(fr, &dst, hdr->proto) > 0) {
total_payload_written = (uint16_t)((uint16_t)fr->transport_len + total_payload_written);
} else {
/* No need to discard frame here, pico_ipv4_frame_push() already did that */
break;
}
} /* while() */
return (int)total_payload_written;
#else
(void)f;
return -1;
#endif
}
int pico_ipv4_rebound(struct pico_frame *f)
{
struct pico_ip4 dst;
struct pico_ipv4_hdr *hdr;
if (!f) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
hdr = (struct pico_ipv4_hdr *) f->net_hdr;
if (!hdr) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
dst.addr = hdr->src.addr;
if (f->transport_len > PICO_IPV4_MAXPAYLOAD) {
return pico_ipv4_rebound_large(f);
}
return pico_ipv4_frame_push(f, &dst, hdr->proto);
}
static int pico_ipv4_pre_forward_checks(struct pico_frame *f)
{
static uint16_t last_id = 0;
static uint16_t last_proto = 0;
static struct pico_ip4 last_src = {
0
};
static struct pico_ip4 last_dst = {
0
};
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr;
/* Decrease TTL, check if expired */
hdr->ttl = (uint8_t)(hdr->ttl - 1);
if (hdr->ttl < 1) {
pico_notify_ttl_expired(f);
dbg(" ------------------- TTL EXPIRED\n");
return -1;
}
/* HACK: increase crc to compensate decreased TTL */
hdr->crc++;
/* If source is local, discard anyway (packets bouncing back and forth) */
if (pico_ipv4_link_get(&hdr->src))
return -1;
/* If this was the last forwarded packet, silently discard to prevent duplications */
if ((last_src.addr == hdr->src.addr) && (last_id == hdr->id)
&& (last_dst.addr == hdr->dst.addr) && (last_proto == hdr->proto)) {
return -1;
} else {
last_src.addr = hdr->src.addr;
last_dst.addr = hdr->dst.addr;
last_id = hdr->id;
last_proto = hdr->proto;
}
return 0;
}
static int pico_ipv4_forward_check_dev(struct pico_frame *f)
{
// TODO What was this for? Clearly causing problems with tap
// if (f->dev->eth != NULL)
// f->len -= PICO_SIZE_ETHHDR;
if (f->len > f->dev->mtu) {
pico_notify_pkt_too_big(f);
return -1;
}
return 0;
}
static int pico_ipv4_forward(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr;
struct pico_ipv4_route *rt;
if (!hdr) {
return -1;
}
rt = route_find(&hdr->dst);
if (!rt) {
pico_notify_dest_unreachable(f);
return -1;
}
f->dev = rt->link->dev;
if (pico_ipv4_pre_forward_checks(f) < 0)
return -1;
pico_ipv4_nat_outbound(f, &rt->link->address);
f->start = f->net_hdr;
if (pico_ipv4_forward_check_dev(f) < 0)
return -1;
pico_datalink_send(f);
return 0;
}
int pico_ipv4_is_broadcast(uint32_t addr)
{
struct pico_ipv4_link *link;
struct pico_tree_node *index;
if (addr == PICO_IP4_BCAST)
return 1;
pico_tree_foreach(index, &Tree_dev_link) {
link = index->keyValue;
if ((link->address.addr | (~link->netmask.addr)) == addr && link->netmask.addr != 0xffffffff)
return 1;
}
return 0;
}
void pico_ipv4_unreachable(struct pico_frame *f, int err)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
#if defined PICO_SUPPORT_TCP || defined PICO_SUPPORT_UDP
f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR;
pico_transport_error(f, hdr->proto, err);
#endif
}
int pico_ipv4_cleanup_links(struct pico_device *dev)
{
struct pico_tree_node *index = NULL, *_tmp = NULL;
struct pico_ipv4_link *link = NULL;
pico_tree_foreach_safe(index, &Tree_dev_link, _tmp) {
link = index->keyValue;
if (dev == link->dev)
pico_ipv4_link_del(dev, link->address);
}
return 0;
}
#endif