Porting PicoTCP WIP
This commit is contained in:
1647
kernel/picotcp/modules/pico_6lowpan.c
Normal file
1647
kernel/picotcp/modules/pico_6lowpan.c
Normal file
File diff suppressed because it is too large
Load Diff
40
kernel/picotcp/modules/pico_6lowpan.h
Normal file
40
kernel/picotcp/modules/pico_6lowpan.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights
|
||||
reserved. See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_6LOWPAN
|
||||
#define INCLUDE_PICO_6LOWPAN
|
||||
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_frame.h"
|
||||
|
||||
#define PICO_6LP_FLAG_LOWPAN (0x01)
|
||||
#define PICO_6LP_FLAG_NOMAC (0x02)
|
||||
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
#define PICO_DEV_IS_6LOWPAN(dev) ((dev) && ((dev)->hostvars.lowpan_flags & PICO_6LP_FLAG_LOWPAN))
|
||||
#define PICO_DEV_IS_NOMAC(dev) ((dev) && ((dev)->hostvars.lowpan_flags & PICO_6LP_FLAG_NOMAC))
|
||||
#else
|
||||
#define PICO_DEV_IS_6LOWPAN(dev) (0)
|
||||
#define PICO_DEV_IS_NOMAC(dev) (0)
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* Public variables
|
||||
******************************************************************************/
|
||||
|
||||
extern struct pico_protocol pico_proto_6lowpan;
|
||||
|
||||
/******************************************************************************
|
||||
* Public functions
|
||||
******************************************************************************/
|
||||
|
||||
int32_t pico_6lowpan_pull(struct pico_frame *f);
|
||||
int pico_6lowpan_init(void);
|
||||
|
||||
#endif /* INCLUDE_PICO_6LOWPAN */
|
||||
454
kernel/picotcp/modules/pico_6lowpan_ll.c
Normal file
454
kernel/picotcp/modules/pico_6lowpan_ll.c
Normal file
@ -0,0 +1,454 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_frame.h"
|
||||
#include "pico_802154.h"
|
||||
#include "pico_6lowpan.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_6lowpan_ll.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
|
||||
/*******************************************************************************
|
||||
* Macros
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef DEBUG_6LOWPAN
|
||||
#define ll_dbg dbg
|
||||
#else
|
||||
#define ll_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Constants
|
||||
******************************************************************************/
|
||||
|
||||
/* Lifetime check interval */
|
||||
#define ONE_MINUTE ((pico_time)(1000 * 60))
|
||||
|
||||
/* Number of extensions */
|
||||
#define NUM_LL_EXTENSIONS (2)
|
||||
|
||||
/*******************************************************************************
|
||||
* Type definitions
|
||||
******************************************************************************/
|
||||
|
||||
struct extension {
|
||||
int32_t (*estimate)(struct pico_frame *f);
|
||||
int32_t (*out)(struct pico_frame *f);
|
||||
int32_t (*in)(struct pico_frame *f);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Global Variables
|
||||
******************************************************************************/
|
||||
|
||||
static const struct pico_6lowpan_ll_protocol pico_6lowpan_ll_none = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
/* Declare a global lookup-table for distribution of link layer specific tasks */
|
||||
struct pico_6lowpan_ll_protocol pico_6lowpan_lls[PICO_6LOWPAN_LLS + 1];
|
||||
|
||||
static struct pico_queue pico_6lowpan_ll_in = {
|
||||
0
|
||||
};
|
||||
static struct pico_queue pico_6lowpan_ll_out = {
|
||||
0
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* CTX
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef PICO_6LOWPAN_IPHC_ENABLED
|
||||
|
||||
/* Compares if the IPv6 prefix of two IPv6 addresses match */
|
||||
static int32_t compare_prefix(uint8_t *a, uint8_t *b, int32_t len)
|
||||
{
|
||||
uint8_t bitmask = (uint8_t)(0xff << (8 - (len % 8)));
|
||||
size_t bytes = (size_t)len / 8;
|
||||
int32_t ret = 0;
|
||||
if ((ret = memcmp(a, b, bytes)))
|
||||
return ret;
|
||||
return (int32_t)((a[bytes] & bitmask) - (b[bytes] & bitmask));
|
||||
}
|
||||
|
||||
/* Compares 2 IPHC context entries */
|
||||
static int32_t compare_ctx(void *a, void *b)
|
||||
{
|
||||
struct iphc_ctx *ca = (struct iphc_ctx *)a;
|
||||
struct iphc_ctx *cb = (struct iphc_ctx *)b;
|
||||
return compare_prefix(ca->prefix.addr, cb->prefix.addr, ca->size);
|
||||
}
|
||||
|
||||
PICO_TREE_DECLARE(CTXtree, compare_ctx);
|
||||
|
||||
/* Searches in the context tree if there's a context entry available with the
|
||||
* prefix of the IPv6 address */
|
||||
struct iphc_ctx * ctx_lookup(struct pico_ip6 addr)
|
||||
{
|
||||
struct iphc_ctx test = { NULL, addr, 0, 0, 0, 0 };
|
||||
return pico_tree_findKey(&CTXtree, &test);
|
||||
}
|
||||
|
||||
/* Looks up the context by ID, for decompression */
|
||||
struct iphc_ctx * ctx_lookup_id(uint8_t id)
|
||||
{
|
||||
struct iphc_ctx *key = NULL;
|
||||
struct pico_tree_node *i = NULL;
|
||||
|
||||
pico_tree_foreach(i, &CTXtree) {
|
||||
key = i->keyValue;
|
||||
if (key && id ==key->id)
|
||||
return key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Tries to insert a new IPHC-context into the Context-tree */
|
||||
static int32_t ctx_insert(struct pico_ip6 addr, uint8_t id, uint8_t size, pico_time lifetime, uint8_t flags, struct pico_device *dev)
|
||||
{
|
||||
struct iphc_ctx *new = PICO_ZALLOC(sizeof(struct iphc_ctx));
|
||||
if (new) {
|
||||
new->lifetime = lifetime;
|
||||
new->prefix = addr;
|
||||
new->flags = flags;
|
||||
new->size = size;
|
||||
new->dev = dev;
|
||||
new->id = id;
|
||||
if (pico_tree_insert(&CTXtree, new)) {
|
||||
PICO_FREE(new);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Function to update context table from 6LoWPAN Neighbor Discovery */
|
||||
void ctx_update(struct pico_ip6 addr, uint8_t id, uint8_t size, pico_time lifetime, uint8_t flags, struct pico_device *dev)
|
||||
{
|
||||
struct iphc_ctx *entry = ctx_lookup_id(id);
|
||||
if (entry && dev == entry->dev) {
|
||||
if (!lifetime) {
|
||||
pico_tree_delete(&CTXtree, entry);
|
||||
PICO_FREE(entry);
|
||||
return;
|
||||
}
|
||||
entry->prefix = addr;
|
||||
entry->size = size;
|
||||
entry->lifetime = lifetime;
|
||||
entry->flags = flags;
|
||||
} else {
|
||||
/* We don't care if it failed */
|
||||
(void)ctx_insert(addr, id, size, lifetime, flags, dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether or not particular contexts are expired and remove them if so. Contexts
|
||||
* are reconfirmed before their lifetime expires */
|
||||
static void ctx_lifetime_check(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_tree_node *i = NULL, *next = NULL;
|
||||
struct pico_ipv6_route *gw = NULL;
|
||||
struct iphc_ctx *key = NULL;
|
||||
IGNORE_PARAMETER(now);
|
||||
IGNORE_PARAMETER(arg);
|
||||
|
||||
pico_tree_foreach_safe(i, &CTXtree, next) {
|
||||
if (i && i->keyValue) {
|
||||
key = i->keyValue;
|
||||
key->lifetime--;
|
||||
if (!key->lifetime) {
|
||||
pico_tree_delete(&CTXtree, key);
|
||||
PICO_FREE(key);
|
||||
} else if (key->lifetime == 5) {
|
||||
/* RFC6775: The host SHOULD unicast one or more RSs to the router well before the
|
||||
* shortest of the, Router Lifetime, PIO lifetimes and the lifetime of the 6COs. */
|
||||
gw = pico_ipv6_gateway_by_dev(key->dev);
|
||||
while (gw) {
|
||||
pico_6lp_nd_start_soliciting(pico_ipv6_linklocal_get(key->dev), gw);
|
||||
gw = pico_ipv6_gateway_by_dev_next(key->dev, gw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)pico_timer_add(ONE_MINUTE, ctx_lifetime_check, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* MESH-UNDER ROUTING LAYER
|
||||
******************************************************************************/
|
||||
|
||||
/* XXX: Extensible processing function for outgoing frames. Here, the mesh header
|
||||
* for a Mesh-Under topology can be prepended and the link layer source and
|
||||
* destination addresses can be updated */
|
||||
static int32_t
|
||||
ll_mesh_header_process_in(struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: Extensible processing function for outgoing frames. Here, the mesh header
|
||||
* for a Mesh-Under topology can be prepended and the link layer source and
|
||||
* destination addresses can be updated */
|
||||
static int32_t
|
||||
ll_mesh_header_process_out(struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: Extensible function that estimates the size of the mesh header to be
|
||||
* prepended based on the frame, the source and destination link layer address */
|
||||
static int32_t
|
||||
ll_mesh_header_estimator(struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* GENERIC 6LOWPAN LINK LAYER
|
||||
******************************************************************************/
|
||||
|
||||
static int32_t
|
||||
ll_mac_header_process_in(struct pico_frame *f)
|
||||
{
|
||||
if (f && f->dev && pico_6lowpan_lls[f->dev->mode].process_in) {
|
||||
return (int32_t)pico_6lowpan_lls[f->dev->mode].process_in(f);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t
|
||||
ll_mac_header_process_out(struct pico_frame *f)
|
||||
{
|
||||
if (f && f->dev && pico_6lowpan_lls[f->dev->mode].process_out) {
|
||||
return (int32_t)pico_6lowpan_lls[f->dev->mode].process_out(f);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t
|
||||
ll_mac_header_estimator(struct pico_frame *f)
|
||||
{
|
||||
if (f && f->dev && pico_6lowpan_lls[f->dev->mode].estimate) {
|
||||
return (int32_t)pico_6lowpan_lls[f->dev->mode].estimate(f);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Alloc's a frame with device's overhead and maximum IEEE802.15.4 header size */
|
||||
static struct pico_frame *
|
||||
pico_6lowpan_frame_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size)
|
||||
{
|
||||
IGNORE_PARAMETER(self);
|
||||
if (dev && pico_6lowpan_lls[dev->mode].alloc) {
|
||||
return pico_6lowpan_lls[dev->mode].alloc(dev, size);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* 6LOWPAN LINK LAYER PROTOCOL
|
||||
******************************************************************************/
|
||||
|
||||
const struct extension exts[] = {
|
||||
{ll_mesh_header_estimator, ll_mesh_header_process_out, ll_mesh_header_process_in},
|
||||
{ll_mac_header_estimator, ll_mac_header_process_out, ll_mac_header_process_in},
|
||||
};
|
||||
|
||||
static int32_t
|
||||
pico_6lowpan_ll_process_out(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
uint32_t datalink_len = 0;
|
||||
int32_t ret = 0, i = 0;
|
||||
IGNORE_PARAMETER(self);
|
||||
|
||||
/* Every link layer extension updates the datalink pointer of the frame a little bit. */
|
||||
f->datalink_hdr = f->net_hdr;
|
||||
|
||||
/* Call each of the outgoing processing functions */
|
||||
for (i = 0; i < NUM_LL_EXTENSIONS; i++) {
|
||||
ret = exts[i].out(f);
|
||||
if (ret < 0) /* Processing failed, no way to recover, discard frame */
|
||||
goto fin;
|
||||
datalink_len = (uint32_t)(datalink_len + (uint32_t)ret);
|
||||
if ((f->net_hdr - datalink_len) < f->buffer) /* Before buffer bound check */
|
||||
goto fin;
|
||||
}
|
||||
|
||||
/* Frame is ready for sending to the device driver */
|
||||
f->start = f->datalink_hdr;
|
||||
f->len = (uint32_t)(f->len + datalink_len);
|
||||
return (int32_t)(pico_sendto_dev(f) <= 0);
|
||||
fin:
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
pico_6lowpan_ll_process_in(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
int32_t i = 0, ret = 0;
|
||||
uint32_t len = 0;
|
||||
IGNORE_PARAMETER(self);
|
||||
|
||||
/* net_hdr is the pointer that is dynamically updated by the incoming
|
||||
* processing functions to always point to right after a particular
|
||||
* header, whether it's MAC, MESH, LL_SEC, ... eventually net_hdr will
|
||||
* point to 6LoWPAN header which is exactly what we want */
|
||||
f->net_hdr = f->buffer;
|
||||
|
||||
for (i = NUM_LL_EXTENSIONS - 1; i >= 0; i--) {
|
||||
ret = exts[i].in(f);
|
||||
switch (ret) {
|
||||
case FRAME_6LOWPAN_LL_RELEASE:
|
||||
/* Success, frame is somewhere else now.. */
|
||||
break;
|
||||
case FRAME_6LOWPAN_LL_DISCARD:
|
||||
/* Something went wrong, discard the frame */
|
||||
pico_frame_discard(f);
|
||||
break;
|
||||
default:
|
||||
/* Success, update link layer header length */
|
||||
len = (uint32_t)(len + (uint32_t)ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine size at network layer */
|
||||
f->net_len = (uint16_t)(f->len - len);
|
||||
f->len = (uint32_t)(f->len - len);
|
||||
return pico_6lowpan_pull(f);
|
||||
}
|
||||
|
||||
/* Entry point for incoming 6LoWPAN frames, proxy for pico_stack_recv. This allows passing the link
|
||||
* layer source and destination address as well */
|
||||
int32_t pico_6lowpan_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len, union pico_ll_addr *src, union pico_ll_addr *dst)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
ll_dbg("6LoWPAN - Stack recv called!\n");
|
||||
if (PICO_DEV_IS_NOMAC(dev)) {
|
||||
struct pico_frame *f = pico_stack_recv_new_frame(dev, buffer, len);
|
||||
if (f) {
|
||||
f->src = *src;
|
||||
f->dst = *dst;
|
||||
ret = pico_enqueue(dev->q_in, f);
|
||||
if (0 >= ret)
|
||||
pico_frame_discard(f);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
return pico_stack_recv(dev, buffer, len);
|
||||
}
|
||||
return -1; // return ERROR
|
||||
}
|
||||
|
||||
/* Proxy for pico_devloop_sendto_dev, 6LoWPAN-devices have a different interface with pico. This
|
||||
* allows passing the link layer source and destination address as well */
|
||||
int32_t pico_6lowpan_ll_sendto_dev(struct pico_device *dev, struct pico_frame *f)
|
||||
{
|
||||
/* FINAL OUTGOING POINT OF 6LOWPAN STACK */
|
||||
return ((struct pico_dev_6lowpan *)dev)->send(dev, f->start, (int32_t)f->len, f->src, f->dst);
|
||||
}
|
||||
|
||||
/* Initialisation routine for 6LoWPAN specific devices */
|
||||
int pico_dev_6lowpan_init(struct pico_dev_6lowpan *dev, const char *name, uint8_t *mac, enum pico_ll_mode ll_mode, uint16_t mtu, uint8_t nomac,
|
||||
int (* send)(struct pico_device *dev, void *_buf, int len, union pico_ll_addr src, union pico_ll_addr dst),
|
||||
int (* poll)(struct pico_device *dev, int loop_score))
|
||||
{
|
||||
struct pico_device *picodev = (struct pico_device *)dev;
|
||||
if (!dev || !send || !poll) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
picodev->mode = ll_mode;
|
||||
picodev->hostvars.lowpan_flags = PICO_6LP_FLAG_LOWPAN;
|
||||
if (nomac) {
|
||||
picodev->hostvars.lowpan_flags |= PICO_6LP_FLAG_NOMAC;
|
||||
}
|
||||
picodev->mtu = mtu;
|
||||
picodev->poll = poll;
|
||||
picodev->send = NULL;
|
||||
dev->send = send;
|
||||
|
||||
return pico_device_init(picodev, name, mac);
|
||||
}
|
||||
|
||||
|
||||
/* Push function for 6LoWPAN to call when it wants to try to send te frame to the device-driver */
|
||||
int32_t
|
||||
pico_6lowpan_ll_push(struct pico_frame *f)
|
||||
{
|
||||
uint16_t frame_size, pl_available = 0;
|
||||
int32_t i = 0;
|
||||
|
||||
if (!f || !f->dev)
|
||||
return -1;
|
||||
frame_size = (uint16_t)(f->len);
|
||||
|
||||
/* Restrict frames to be as large as the device's MTU. */
|
||||
pl_available = (uint16_t)f->dev->mtu;
|
||||
|
||||
/* Call each of the estimator functions of the additional headers to
|
||||
* determine if the frame fits inside a single 802.15.4 frame, if it doesn't
|
||||
* in the end, return the available bytes */
|
||||
for (i = 0; i < NUM_LL_EXTENSIONS; i++) {
|
||||
pl_available = (uint16_t)(pl_available - exts[i].estimate(f));
|
||||
}
|
||||
if (frame_size > pl_available)
|
||||
return pl_available;
|
||||
|
||||
/* Make sure these addresses are retrievable from the frame on processing */
|
||||
if (pico_enqueue(pico_proto_6lowpan_ll.q_out,f) > 0) {
|
||||
return 0; // Frame enqueued for later processing
|
||||
}
|
||||
return -1; // Return ERROR
|
||||
}
|
||||
|
||||
struct pico_protocol pico_proto_6lowpan_ll = {
|
||||
.name = "6lowpan_ll",
|
||||
.layer = PICO_LAYER_DATALINK,
|
||||
.alloc = pico_6lowpan_frame_alloc,
|
||||
.process_in = pico_6lowpan_ll_process_in,
|
||||
.process_out = pico_6lowpan_ll_process_out,
|
||||
.q_in = &pico_6lowpan_ll_in,
|
||||
.q_out = &pico_6lowpan_ll_out
|
||||
};
|
||||
|
||||
void pico_6lowpan_ll_init(void)
|
||||
{
|
||||
int32_t i = 0;
|
||||
|
||||
#ifdef PICO_6LOWPAN_IPHC_ENABLED
|
||||
/* We don't care about failure */
|
||||
(void)pico_timer_add(60000, ctx_lifetime_check, NULL);
|
||||
#endif
|
||||
|
||||
/* Initialize interface with 6LoWPAN link layer protocols */
|
||||
pico_6lowpan_lls[i++] = pico_6lowpan_ll_none;
|
||||
|
||||
#ifdef PICO_SUPPORT_802154
|
||||
pico_6lowpan_lls[i++] = pico_6lowpan_ll_802154;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* PICO_SUPPORT_6LOWPAN */
|
||||
122
kernel/picotcp/modules/pico_6lowpan_ll.h
Normal file
122
kernel/picotcp/modules/pico_6lowpan_ll.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights
|
||||
reserved. See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_6LOWPAN_LL
|
||||
#define INCLUDE_PICO_6LOWPAN_LL
|
||||
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_6lowpan.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_frame.h"
|
||||
#include "pico_ipv6.h"
|
||||
|
||||
/* Possible actions to perform on a received frame */
|
||||
#define FRAME_6LOWPAN_LL_RELEASE (-1)
|
||||
#define FRAME_6LOWPAN_LL_DISCARD (-2)
|
||||
|
||||
/*******************************************************************************
|
||||
* CTX
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef PICO_6LOWPAN_IPHC_ENABLED
|
||||
|
||||
#define PICO_IPHC_CTX_COMPRESS (0x01u)
|
||||
|
||||
struct iphc_ctx
|
||||
{
|
||||
struct pico_device *dev;
|
||||
struct pico_ip6 prefix;
|
||||
uint8_t id;
|
||||
uint8_t size;
|
||||
uint8_t flags;
|
||||
pico_time lifetime;
|
||||
};
|
||||
|
||||
/*
|
||||
* Looks up a context entry for a particular IPv6-address contained in 'addr' and returns it.
|
||||
* Returns NULL if no entry is found. (See RFC4944)
|
||||
*/
|
||||
struct iphc_ctx * ctx_lookup(struct pico_ip6 addr);
|
||||
|
||||
/*
|
||||
* Looks up a context entry that belongs to a certain context identifier.
|
||||
* Returns NULL if no belonging entry is found. (See RFC4944)
|
||||
*/
|
||||
struct iphc_ctx * ctx_lookup_id(uint8_t id);
|
||||
|
||||
/*
|
||||
* Creates a new, or updates and existing, context entry for a certain IPv6 address. (See RFC4944)
|
||||
*/
|
||||
void ctx_update(struct pico_ip6 addr, uint8_t id, uint8_t size, pico_time lifetime, uint8_t flags, struct pico_device *dev);
|
||||
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* Interface with device drivers
|
||||
******************************************************************************/
|
||||
|
||||
struct pico_dev_6lowpan
|
||||
{
|
||||
/* Interface with picoTCP */
|
||||
struct pico_device dev;
|
||||
|
||||
/* Transmit-function:
|
||||
*
|
||||
* @param dev The device who's send-function got called
|
||||
* @param _buf Buffer containing the frame to be send over the network
|
||||
* @param len Length of _buf
|
||||
* @param src Link Layer source address of the device (IETF-endianness)
|
||||
* @param dst Link layer destination address of the device (IETF-endianness)
|
||||
*
|
||||
* @return length of the frame that is transmitted on success, -1 on failure
|
||||
*/
|
||||
int (* send)(struct pico_device *dev, void *_buf, int len, union pico_ll_addr src, union pico_ll_addr dst);
|
||||
};
|
||||
|
||||
/* Initialisation routine for 6LoWPAN specific devices */
|
||||
int pico_dev_6lowpan_init(struct pico_dev_6lowpan *dev, const char *name, uint8_t *mac, enum pico_ll_mode ll_mode, uint16_t mtu, uint8_t nomac,
|
||||
int (* send)(struct pico_device *dev, void *_buf, int len, union pico_ll_addr src, union pico_ll_addr dst),
|
||||
int (* poll)(struct pico_device *dev, int loop_score));
|
||||
|
||||
/******************************************************************************
|
||||
* Interface with link layer
|
||||
******************************************************************************/
|
||||
|
||||
struct pico_6lowpan_ll_protocol
|
||||
{
|
||||
int32_t (* process_in)(struct pico_frame *f);
|
||||
int32_t (* process_out)(struct pico_frame *f);
|
||||
int32_t (* estimate)(struct pico_frame *f);
|
||||
int32_t (* addr_from_buf)(union pico_ll_addr *addr, uint8_t *buf);
|
||||
int32_t (* addr_from_net)(union pico_ll_addr *addr, struct pico_frame *f, int32_t dest);
|
||||
int32_t (* addr_len)(union pico_ll_addr *addr);
|
||||
int32_t (* addr_cmp)(union pico_ll_addr *a, union pico_ll_addr *b);
|
||||
int32_t (* addr_iid)(uint8_t *iid, union pico_ll_addr *addr);
|
||||
struct pico_frame * (*alloc)(struct pico_device *dev, uint16_t size);
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Public variables
|
||||
******************************************************************************/
|
||||
|
||||
extern struct pico_6lowpan_ll_protocol pico_6lowpan_lls[];
|
||||
extern struct pico_protocol pico_proto_6lowpan_ll;
|
||||
|
||||
/******************************************************************************
|
||||
* Public functions
|
||||
******************************************************************************/
|
||||
|
||||
void pico_6lowpan_ll_init(void);
|
||||
int32_t pico_6lowpan_ll_push(struct pico_frame *f);
|
||||
int32_t pico_6lowpan_ll_pull(struct pico_frame *f);
|
||||
int32_t frame_6lowpan_ll_store_addr(struct pico_frame *f);
|
||||
int32_t pico_6lowpan_ll_sendto_dev(struct pico_device *dev, struct pico_frame *f);
|
||||
int32_t pico_6lowpan_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len, union pico_ll_addr *src, union pico_ll_addr *dst);
|
||||
|
||||
#endif /* INCLUDE_PICO_6LOWPAN_LL */
|
||||
456
kernel/picotcp/modules/pico_802154.c
Normal file
456
kernel/picotcp/modules/pico_802154.c
Normal file
@ -0,0 +1,456 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights
|
||||
reserved. See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_stack.h"
|
||||
#include "pico_frame.h"
|
||||
#include "pico_802154.h"
|
||||
#include "pico_6lowpan.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_6lowpan_ll.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_802154
|
||||
|
||||
/*******************************************************************************
|
||||
* Macros
|
||||
******************************************************************************/
|
||||
|
||||
#define PICO_802154_VALID(am) ((am) == 2 || (am) == 3 ? 1 : 0)
|
||||
|
||||
/*******************************************************************************
|
||||
* Constants
|
||||
******************************************************************************/
|
||||
|
||||
/* Frame type definitions */
|
||||
#define FCF_TYPE_BEACON (short_be(0x0000u))
|
||||
#define FCF_TYPE_DATA (short_be(0x0001u))
|
||||
#define FCF_TYPE_ACK (short_be(0x0002u))
|
||||
#define FCF_TYPE_CMD (short_be(0x0003u))
|
||||
|
||||
/* Frame version definitions */
|
||||
#define FCF_VER_2003 (short_be(0x0000u))
|
||||
#define FCF_VER_2006 (short_be(0x1000u))
|
||||
#define FCF_SEC (short_be(0x0008u))
|
||||
#define FCF_NO_SEC (short_be(0x0000u))
|
||||
#define FCF_PENDING (short_be(0x0010u))
|
||||
#define FCF_NO_PENDING (short_be(0x0000u))
|
||||
#define FCF_ACK_REQ (short_be(0x0020u))
|
||||
#define FCF_NO_ACK_REQ (short_be(0x0000u))
|
||||
#define FCF_INTRA_PAN (short_be(0x0040u))
|
||||
#define FCF_INTER_PAN (short_be(0x0000u))
|
||||
|
||||
/* Commonly used addresses */
|
||||
#define ADDR_802154_BCAST (short_be(0xFFFFu))
|
||||
#define ADDR_802154_UNSPEC (short_be(0xFFFEu))
|
||||
|
||||
#ifndef PICO_6LOWPAN_NOMAC
|
||||
|
||||
/*******************************************************************************
|
||||
* ENDIANNESS
|
||||
******************************************************************************/
|
||||
|
||||
/* Swaps the two 8-bit values, the pointer A and B point at */
|
||||
static void pico_swap(uint8_t *a, uint8_t *b)
|
||||
{
|
||||
*a = *a ^ *b;
|
||||
*b = *a ^ *b;
|
||||
*a = *a ^ *b;
|
||||
}
|
||||
|
||||
/* Converts an IEEE802.15.4 address, which is little endian by standard, to
|
||||
* IETF-endianness, which is big endian. */
|
||||
static void
|
||||
addr_802154_to_ietf(struct pico_802154 *addr)
|
||||
{
|
||||
int32_t i = 0;
|
||||
int32_t end = SIZE_6LOWPAN(addr->mode) - 1;
|
||||
for (i = 0; i < (int32_t)((uint8_t)SIZE_6LOWPAN(addr->mode) >> 1); i++) {
|
||||
pico_swap(&addr->addr.data[i], &addr->addr.data[end - i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Converts an IEE802.15.4 address in IETF format, which is used to form the IID
|
||||
* of the host's IPv6 addresses, back to IEEE-endianess, which is little
|
||||
* endian. */
|
||||
static void
|
||||
addr_802154_to_ieee(struct pico_802154 *addr)
|
||||
{
|
||||
addr_802154_to_ietf(addr);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* FRAME
|
||||
******************************************************************************/
|
||||
|
||||
/* Retrieves the addressing mode of the destination address from the MHR's frame
|
||||
* control field. */
|
||||
static uint8_t
|
||||
dst_am(struct pico_802154_hdr *hdr)
|
||||
{
|
||||
return (uint8_t)((hdr->fcf >> 10) & 0x3);
|
||||
}
|
||||
|
||||
/* Retrieves the addressing mode of the source address from the MHR's frame
|
||||
* control field */
|
||||
static uint8_t
|
||||
src_am(struct pico_802154_hdr *hdr)
|
||||
{
|
||||
return (uint8_t)((hdr->fcf >> 14) & 0x3);
|
||||
}
|
||||
|
||||
/* Determines the size of an IEEE802.15.4-header, based on the addressing
|
||||
* modes */
|
||||
static uint8_t
|
||||
frame_802154_hdr_len(struct pico_802154_hdr *hdr)
|
||||
{
|
||||
return (uint8_t)(SIZE_802154_MHR_MIN + SIZE_6LOWPAN(src_am(hdr)) + SIZE_6LOWPAN(dst_am(hdr)));
|
||||
}
|
||||
|
||||
/* Gets the source address out of a mapped IEEE802.15.4-frame, converts it
|
||||
* to host endianess */
|
||||
static struct pico_802154
|
||||
frame_802154_src(struct pico_802154_hdr *hdr)
|
||||
{
|
||||
struct pico_802154 src = { .addr.data = { 0 }, .mode = src_am(hdr) };
|
||||
uint8_t *addresses = (uint8_t *)hdr + sizeof(struct pico_802154_hdr);
|
||||
uint16_t len = SIZE_6LOWPAN(src.mode);
|
||||
memcpy(src.addr.data, addresses + SIZE_6LOWPAN(dst_am(hdr)), len);
|
||||
addr_802154_to_ietf(&src);
|
||||
return src;
|
||||
}
|
||||
|
||||
/* Gets the destination address out of a mapped IEEE802.15.4-frame, converts
|
||||
* it to host endianess */
|
||||
static struct pico_802154
|
||||
frame_802154_dst(struct pico_802154_hdr *hdr)
|
||||
{
|
||||
struct pico_802154 dst = { .addr.data = { 0 }, .mode = dst_am(hdr) };
|
||||
uint8_t *addresses = (uint8_t *)hdr + sizeof(struct pico_802154_hdr);
|
||||
uint16_t len = SIZE_6LOWPAN(dst.mode);
|
||||
memcpy(dst.addr.data, addresses, len);
|
||||
addr_802154_to_ietf(&dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* Maps a 802.15.4 frame structure onto a flat buffer, fills in the entire
|
||||
* header and set the payload pointer right after the MHR. */
|
||||
static void
|
||||
frame_802154_format(uint8_t *buf, uint8_t seq, uint16_t intra_pan, uint16_t ack,
|
||||
uint16_t sec, struct pico_6lowpan_short pan, struct pico_802154 src,
|
||||
struct pico_802154 dst)
|
||||
{
|
||||
uint8_t *addresses = (uint8_t *)(buf + sizeof(struct pico_802154_hdr));
|
||||
struct pico_802154_hdr *hdr = (struct pico_802154_hdr *)buf;
|
||||
uint16_t sam = 0, dam = 0;
|
||||
|
||||
hdr->fcf = 0; /* Clear out control field */
|
||||
intra_pan = (uint16_t)(intra_pan & FCF_INTRA_PAN);
|
||||
ack = (uint16_t)(ack & FCF_ACK_REQ);
|
||||
sec = (uint16_t)(sec & FCF_SEC);
|
||||
dam = short_be((uint16_t)(dst.mode << 10));
|
||||
sam = short_be((uint16_t)(src.mode << 14));
|
||||
|
||||
/* Fill in frame control field */
|
||||
hdr->fcf |= (uint16_t)(FCF_TYPE_DATA | sec );
|
||||
hdr->fcf |= (uint16_t)(FCF_NO_PENDING | ack);
|
||||
hdr->fcf |= (uint16_t)(intra_pan | dam | FCF_VER_2003);
|
||||
hdr->fcf |= (uint16_t)(sam);
|
||||
hdr->fcf = short_be(hdr->fcf); // Convert to IEEE endianness
|
||||
|
||||
hdr->seq = seq; // Sequence number
|
||||
|
||||
/* Convert addresses to IEEE-endianness */
|
||||
pan.addr = short_be(pan.addr);
|
||||
addr_802154_to_ieee(&src);
|
||||
addr_802154_to_ieee(&dst);
|
||||
|
||||
/* Fill in the addresses */
|
||||
memcpy(&hdr->pan_id, &pan.addr, SIZE_6LOWPAN_SHORT);
|
||||
memcpy(addresses, dst.addr.data, SIZE_6LOWPAN(dst.mode));
|
||||
memcpy(addresses + SIZE_6LOWPAN(dst.mode), src.addr.data,SIZE_6LOWPAN(src.mode));
|
||||
}
|
||||
|
||||
#endif /* PICO_6LOWPAN_NOMAC */
|
||||
|
||||
/* Removes the IEEE802.15.4 MAC header before the frame */
|
||||
static int32_t
|
||||
pico_802154_process_in(struct pico_frame *f)
|
||||
{
|
||||
#ifndef PICO_6LOWPAN_NOMAC
|
||||
struct pico_802154_hdr *hdr = (struct pico_802154_hdr *)f->net_hdr;
|
||||
uint16_t fcf = short_be(hdr->fcf);
|
||||
uint8_t hlen = 0;
|
||||
f->src.pan = frame_802154_src(hdr);
|
||||
f->dst.pan = frame_802154_dst(hdr);
|
||||
|
||||
/* I claim the datalink header */
|
||||
f->datalink_hdr = f->net_hdr;
|
||||
|
||||
if (fcf & FCF_SEC) {
|
||||
f->flags |= PICO_FRAME_FLAG_LL_SEC;
|
||||
}
|
||||
|
||||
hlen = frame_802154_hdr_len(hdr);
|
||||
|
||||
/* XXX: Generic procedure to move forward in incoming processing function
|
||||
* is updating the net_hdr-pointer */
|
||||
f->net_hdr = f->datalink_hdr + (int32_t)hlen;
|
||||
|
||||
return (int32_t)hlen;
|
||||
#else
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Prepends the IEEE802.15.4 MAC header before the frame */
|
||||
static int32_t
|
||||
pico_802154_process_out(struct pico_frame *f)
|
||||
{
|
||||
#ifndef PICO_6LOWPAN_NOMAC
|
||||
int32_t len = (int32_t)(SIZE_802154_MHR_MIN + SIZE_6LOWPAN(f->dst.pan.mode) + SIZE_6LOWPAN(f->src.pan.mode));
|
||||
uint8_t sec = (uint8_t)((f->flags & PICO_FRAME_FLAG_LL_SEC) ? (FCF_SEC) : (FCF_NO_SEC));
|
||||
struct pico_6lowpan_info *info = (struct pico_6lowpan_info *)f->dev->eth;
|
||||
uint16_t headroom = (uint16_t)(f->net_hdr - f->buffer);
|
||||
static uint8_t seq = 0;
|
||||
uint32_t grow = 0;
|
||||
int32_t ret = 0;
|
||||
|
||||
if (headroom < (uint16_t)len) { /* Check if there's enough headroom to prepend 802.15.4 header */
|
||||
grow = (uint32_t)(len - headroom);
|
||||
ret = pico_frame_grow_head(f, (uint32_t)(f->buffer_len + grow));
|
||||
if (ret) {
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: General procedure to seek backward in an outgoing processing function
|
||||
* is to update the datalink_hdr */
|
||||
f->datalink_hdr = f->datalink_hdr - len;
|
||||
|
||||
/* Format the IEEE802.15.4 header */
|
||||
frame_802154_format(f->datalink_hdr, seq++, FCF_INTRA_PAN, FCF_NO_ACK_REQ, sec, info->pan_id, f->src.pan, f->dst.pan);
|
||||
return len;
|
||||
#else
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get the EUI-64 of the device in a structured form */
|
||||
static struct pico_802154
|
||||
addr_802154_ext_dev(struct pico_6lowpan_info *info)
|
||||
{
|
||||
struct pico_802154 addr;
|
||||
memcpy(addr.addr.data, info->addr_ext.addr, SIZE_6LOWPAN_EXT);
|
||||
addr.mode = AM_6LOWPAN_EXT;
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Get the short address of the device in a structured form */
|
||||
static struct pico_802154
|
||||
addr_802154_short_dev(struct pico_6lowpan_info *info)
|
||||
{
|
||||
struct pico_802154 addr;
|
||||
memcpy(addr.addr.data, (uint8_t *)&(info->addr_short.addr), SIZE_6LOWPAN_SHORT);
|
||||
addr.mode = AM_6LOWPAN_SHORT;
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Based on the source IPv6-address, this function derives the link layer source
|
||||
* address */
|
||||
static struct pico_802154
|
||||
addr_802154_ll_src(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ip6 src = ((struct pico_ipv6_hdr *)f->net_hdr)->src;
|
||||
if (IID_16(&src.addr[8])) {
|
||||
/* IPv6 source is derived from the device's short address, use that
|
||||
* short address so decompressor can derive the IPv6 source from
|
||||
* the encapsulating header */
|
||||
return addr_802154_short_dev((struct pico_6lowpan_info *)f->dev->eth);
|
||||
} else {
|
||||
/* IPv6 source is derived from the device's extended address, use
|
||||
* the device's extended address so */
|
||||
return addr_802154_ext_dev((struct pico_6lowpan_info *)f->dev->eth);
|
||||
}
|
||||
}
|
||||
|
||||
/* Based on the destination IPv6-address, this function derives the link layer
|
||||
* destination address */
|
||||
static struct pico_802154
|
||||
addr_802154_ll_dst(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ip6 dst = ((struct pico_ipv6_hdr *)f->net_hdr)->dst;
|
||||
struct pico_802154 addr = { .addr.data = { 0 }, .mode = 0 };
|
||||
addr.mode = AM_6LOWPAN_NONE;
|
||||
|
||||
/* If the address is multicast use 802.15.4 BCAST address 0xFFFF */
|
||||
if (pico_ipv6_is_multicast(dst.addr)) {
|
||||
addr.addr._short.addr = short_be(ADDR_802154_BCAST);
|
||||
addr.mode = AM_6LOWPAN_SHORT;
|
||||
}
|
||||
/* If the address is link local derive the link layer address from the IID */
|
||||
else { // if (pico_ipv6_is_linklocal(dst.addr)) {
|
||||
if (IID_16(&dst.addr[8])) {
|
||||
addr.addr.data[0] = dst.addr[14];
|
||||
addr.addr.data[1] = dst.addr[15];
|
||||
addr.mode = AM_6LOWPAN_SHORT;
|
||||
} else {
|
||||
memcpy(addr.addr.data, &dst.addr[8], SIZE_6LOWPAN_EXT);
|
||||
addr.addr.data[0] = (uint8_t)(addr.addr.data[0] ^ 0x02);
|
||||
addr.mode = AM_6LOWPAN_EXT;
|
||||
}
|
||||
}
|
||||
/*
|
||||
else {
|
||||
struct pico_802154 *n = (struct pico_802154 *)pico_ipv6_get_neighbor(f);
|
||||
if (n) {
|
||||
memcpy(addr.addr.data, n->addr.data, SIZE_6LOWPAN(n->mode));
|
||||
addr.mode = n->mode;
|
||||
} else {
|
||||
pico_ipv6_nd_postpone(f);
|
||||
}
|
||||
}
|
||||
*/
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Estimates the size the MAC header would be based on the source and destination
|
||||
* link layer address */
|
||||
static int32_t
|
||||
pico_802154_estimator(struct pico_frame *f)
|
||||
{
|
||||
return (int32_t)(SIZE_802154_MHR_MIN + SIZE_6LOWPAN(f->src.pan.mode) + SIZE_6LOWPAN(f->dst.pan.mode) + f->dev->overhead);
|
||||
}
|
||||
|
||||
/* Retrieve address from temporarily flat buffer */
|
||||
static int32_t
|
||||
addr_802154_from_buf(union pico_ll_addr *addr, uint8_t *buf)
|
||||
{
|
||||
uint8_t len = (uint8_t)*buf++;
|
||||
|
||||
if (len > 8) // OOB check
|
||||
return -1;
|
||||
|
||||
memcpy(addr->pan.addr.data, buf, len);
|
||||
if (SIZE_6LOWPAN_EXT == len)
|
||||
addr->pan.mode = AM_6LOWPAN_EXT;
|
||||
else if (SIZE_6LOWPAN_SHORT == len)
|
||||
addr->pan.mode = AM_6LOWPAN_SHORT;
|
||||
else
|
||||
addr->pan.mode = AM_6LOWPAN_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If 'dest' is not set, this function will get the link layer address for a
|
||||
* certain source IPv6 address, if 'dest' is set it will get it for the a
|
||||
* destination address */
|
||||
static int32_t
|
||||
addr_802154_from_net(union pico_ll_addr *addr, struct pico_frame *f, int32_t dest)
|
||||
{
|
||||
if (dest) {
|
||||
addr->pan = addr_802154_ll_dst(f);
|
||||
} else {
|
||||
addr->pan = addr_802154_ll_src(f);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determines the length of an IEEE802.15.4 address */
|
||||
static int32_t
|
||||
addr_802154_len(union pico_ll_addr *addr)
|
||||
{
|
||||
return SIZE_6LOWPAN(addr->pan.mode);
|
||||
}
|
||||
|
||||
/* Compares 2 IEE802.15.4 addresses */
|
||||
static int32_t
|
||||
addr_802154_cmp(union pico_ll_addr *a, union pico_ll_addr *b)
|
||||
{
|
||||
if (a->pan.mode != b->pan.mode) {
|
||||
return (int32_t)((int32_t)a->pan.mode - (int32_t)b->pan.mode);
|
||||
} else {
|
||||
return memcmp(a->pan.addr.data, b->pan.addr.data, SIZE_6LOWPAN(b->pan.mode));
|
||||
}
|
||||
}
|
||||
|
||||
/* Derive an IPv6 IID from an IEEE802.15.4 address */
|
||||
static int32_t
|
||||
addr_802154_iid(uint8_t iid[8], union pico_ll_addr *addr)
|
||||
{
|
||||
uint8_t buf[8] = {0,0,0,0xff,0xfe,0,0,0};
|
||||
struct pico_802154 pan = addr->pan;
|
||||
|
||||
if (AM_6LOWPAN_SHORT == pan.mode) {
|
||||
buf[6] = (uint8_t)(pan.addr._short.addr);
|
||||
buf[7] = (uint8_t)(pan.addr._short.addr >> 8);
|
||||
} else if (AM_6LOWPAN_EXT == pan.mode) {
|
||||
memcpy(buf, pan.addr.data, SIZE_6LOWPAN_EXT);
|
||||
buf[0] ^= (uint8_t)0x02;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(iid, buf, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates a pico_frame but makes sure the network-buffer starts on an 4-byte aligned address,
|
||||
* this is required by upper layer of the stack. IEEE802.15.4's header isn't necessarily 4/8-byte
|
||||
* aligned since the minimum size of an IEEE802.15.4 header is '5'. The datalink header therefore
|
||||
* might not (and most probably isn't) aligned on an aligned address. The datalink header will of
|
||||
* the size passed in 'headroom'
|
||||
*
|
||||
* @param size Size of the actual frame provided for network-layer and above
|
||||
* @param headroom Size of the headroom for datalink-buffer
|
||||
* @param overhead Size of the overhead to keep for the device driver
|
||||
*
|
||||
* @return struct pico_frame *, returns the allocated frame upon success, 'NULL' otherwise.
|
||||
*/
|
||||
static struct pico_frame *
|
||||
pico_frame_alloc_with_headroom(uint16_t size, uint16_t headroom, uint16_t overhead)
|
||||
{
|
||||
int network_offset = (((headroom + overhead) >> 2) + 1) << 2; // Sufficient headroom for alignment
|
||||
struct pico_frame *f = pico_frame_alloc((uint32_t)(size + network_offset));
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
f->net_hdr = f->buffer + network_offset;
|
||||
f->datalink_hdr = f->net_hdr - headroom;
|
||||
return f;
|
||||
}
|
||||
|
||||
/* Allocates a frame with the maximum MAC header size + device's overhead-parameter since this is
|
||||
* the lowest level of the frame allocation chain */
|
||||
static struct pico_frame *
|
||||
pico_802154_frame_alloc(struct pico_device *dev, uint16_t size)
|
||||
{
|
||||
struct pico_frame *f = pico_frame_alloc_with_headroom(size, SIZE_802154_MHR_MAX, (uint16_t)dev->overhead);
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
f->dev = dev;
|
||||
return f;
|
||||
}
|
||||
|
||||
const struct pico_6lowpan_ll_protocol pico_6lowpan_ll_802154 = {
|
||||
.process_in = pico_802154_process_in,
|
||||
.process_out = pico_802154_process_out,
|
||||
.estimate = pico_802154_estimator,
|
||||
.addr_from_buf = addr_802154_from_buf,
|
||||
.addr_from_net = addr_802154_from_net,
|
||||
.addr_len = addr_802154_len,
|
||||
.addr_cmp = addr_802154_cmp,
|
||||
.addr_iid = addr_802154_iid,
|
||||
.alloc = pico_802154_frame_alloc,
|
||||
};
|
||||
|
||||
#endif /* PICO_SUPPORT_802154 */
|
||||
40
kernel/picotcp/modules/pico_802154.h
Normal file
40
kernel/picotcp/modules/pico_802154.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights
|
||||
reserved. See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_802154
|
||||
#define INCLUDE_PICO_802154
|
||||
|
||||
#include "pico_device.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_6lowpan_ll.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* Size definitions
|
||||
******************************************************************************/
|
||||
|
||||
#define MTU_802154_PHY (128u)
|
||||
#define MTU_802154_MAC (125u) // 127 - Frame Check Sequence
|
||||
|
||||
#define SIZE_802154_MHR_MIN (5u)
|
||||
#define SIZE_802154_MHR_MAX (23u)
|
||||
#define SIZE_802154_FCS (2u)
|
||||
#define SIZE_802154_LEN (1u)
|
||||
#define SIZE_802154_PAN (2u)
|
||||
|
||||
/*******************************************************************************
|
||||
* Structure definitions
|
||||
******************************************************************************/
|
||||
|
||||
PACKED_STRUCT_DEF pico_802154_hdr
|
||||
{
|
||||
uint16_t fcf;
|
||||
uint8_t seq;
|
||||
uint16_t pan_id;
|
||||
};
|
||||
|
||||
extern const struct pico_6lowpan_ll_protocol pico_6lowpan_ll_802154;
|
||||
|
||||
#endif /* INCLUDE_PICO_802154 */
|
||||
696
kernel/picotcp/modules/pico_aodv.c
Normal file
696
kernel/picotcp/modules/pico_aodv.c
Normal file
@ -0,0 +1,696 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2015-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Author: Daniele Lacamera <daniele.lacamera@altran.com>
|
||||
*********************************************************************/
|
||||
#include <pico_stack.h>
|
||||
#include <pico_tree.h>
|
||||
#include <pico_socket.h>
|
||||
#include <pico_aodv.h>
|
||||
#include <pico_device.h>
|
||||
|
||||
#include <pico_ipv4.h>
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
|
||||
#ifdef DEBUG_AODV
|
||||
#define pico_aodv_dbg dbg
|
||||
#else
|
||||
#define pico_aodv_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define AODV_MAX_PKT (64)
|
||||
static const struct pico_ip4 HOST_NETMASK = {
|
||||
0xffffffff
|
||||
};
|
||||
static struct pico_ip4 all_bcast = {
|
||||
.addr = 0xFFFFFFFFu
|
||||
};
|
||||
|
||||
static const struct pico_ip4 ANY_HOST = {
|
||||
0x0
|
||||
};
|
||||
|
||||
static uint32_t pico_aodv_local_id = 0;
|
||||
static int aodv_node_compare(void *ka, void *kb)
|
||||
{
|
||||
struct pico_aodv_node *a = ka, *b = kb;
|
||||
if (a->dest.ip4.addr < b->dest.ip4.addr)
|
||||
return -1;
|
||||
|
||||
if (b->dest.ip4.addr < a->dest.ip4.addr)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aodv_dev_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_device *a = ka, *b = kb;
|
||||
if (a->hash < b->hash)
|
||||
return -1;
|
||||
|
||||
if (a->hash > b->hash)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PICO_TREE_DECLARE(aodv_nodes, aodv_node_compare);
|
||||
static PICO_TREE_DECLARE(aodv_devices, aodv_dev_cmp);
|
||||
|
||||
static struct pico_socket *aodv_socket = NULL;
|
||||
|
||||
static struct pico_aodv_node *get_node_by_addr(const union pico_address *addr)
|
||||
{
|
||||
struct pico_aodv_node search;
|
||||
memcpy(&search.dest, addr, sizeof(union pico_address));
|
||||
return pico_tree_findKey(&aodv_nodes, &search);
|
||||
|
||||
}
|
||||
|
||||
static void pico_aodv_set_dev(struct pico_device *dev)
|
||||
{
|
||||
pico_ipv4_route_set_bcast_link(pico_ipv4_link_by_dev(dev));
|
||||
}
|
||||
|
||||
|
||||
static int aodv_peer_refresh(struct pico_aodv_node *node, uint32_t seq)
|
||||
{
|
||||
if ((0 == (node->flags & PICO_AODV_NODE_SYNC)) || (pico_seq_compare(seq, node->dseq) > 0)) {
|
||||
node->dseq = seq;
|
||||
node->flags |= PICO_AODV_NODE_SYNC;
|
||||
node->last_seen = PICO_TIME_MS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void aodv_elect_route(struct pico_aodv_node *node, union pico_address *gw, uint8_t metric, struct pico_device *dev)
|
||||
{
|
||||
metric++;
|
||||
if (!(PICO_AODV_ACTIVE(node)) || metric < node->metric) {
|
||||
pico_ipv4_route_del(node->dest.ip4, HOST_NETMASK, node->metric);
|
||||
if (!gw) {
|
||||
pico_ipv4_route_add(node->dest.ip4, HOST_NETMASK, ANY_HOST, 1, pico_ipv4_link_by_dev(dev));
|
||||
node->metric = 1;
|
||||
} else {
|
||||
node->metric = metric;
|
||||
pico_ipv4_route_add(node->dest.ip4, HOST_NETMASK, gw->ip4, metric, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct pico_aodv_node *aodv_peer_new(const union pico_address *addr)
|
||||
{
|
||||
struct pico_aodv_node *node = PICO_ZALLOC(sizeof(struct pico_aodv_node));
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
memcpy(&node->dest, addr, sizeof(union pico_address));
|
||||
|
||||
if (pico_tree_insert(&aodv_nodes, node)) {
|
||||
PICO_FREE(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
static struct pico_aodv_node *aodv_peer_eval(union pico_address *addr, uint32_t seq, int valid_seq)
|
||||
{
|
||||
struct pico_aodv_node *node = NULL;
|
||||
node = get_node_by_addr(addr);
|
||||
if (!node) {
|
||||
node = aodv_peer_new(addr);
|
||||
}
|
||||
|
||||
if (!valid_seq)
|
||||
return node;
|
||||
|
||||
if (node && (aodv_peer_refresh(node, long_be(seq)) == 0))
|
||||
return node;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void aodv_forward(void *pkt, struct pico_msginfo *info, int reply)
|
||||
{
|
||||
struct pico_aodv_node *orig;
|
||||
union pico_address orig_addr;
|
||||
struct pico_tree_node *index;
|
||||
struct pico_device *dev;
|
||||
pico_time now;
|
||||
int size;
|
||||
|
||||
pico_aodv_dbg("Forwarding %s packet\n", reply ? "REPLY" : "REQUEST");
|
||||
|
||||
if (reply) {
|
||||
struct pico_aodv_rrep *rep = (struct pico_aodv_rrep *)pkt;
|
||||
orig_addr.ip4.addr = rep->dest;
|
||||
rep->hop_count++;
|
||||
pico_aodv_dbg("RREP hop count: %d\n", rep->hop_count);
|
||||
size = sizeof(struct pico_aodv_rrep);
|
||||
} else {
|
||||
struct pico_aodv_rreq *req = (struct pico_aodv_rreq *)pkt;
|
||||
orig_addr.ip4.addr = req->orig;
|
||||
req->hop_count++;
|
||||
size = sizeof(struct pico_aodv_rreq);
|
||||
}
|
||||
|
||||
orig = get_node_by_addr(&orig_addr);
|
||||
if (!orig)
|
||||
orig = aodv_peer_new(&orig_addr);
|
||||
|
||||
if (!orig)
|
||||
return;
|
||||
|
||||
now = PICO_TIME_MS();
|
||||
|
||||
pico_aodv_dbg("Forwarding %s: last fwd_time: %lu now: %lu ttl: %d ==== \n", reply ? "REPLY" : "REQUEST", orig->fwd_time, now, info->ttl);
|
||||
if (((orig->fwd_time == 0) || ((now - orig->fwd_time) > AODV_NODE_TRAVERSAL_TIME)) && (--info->ttl > 0)) {
|
||||
orig->fwd_time = now;
|
||||
info->dev = NULL;
|
||||
pico_tree_foreach(index, &aodv_devices){
|
||||
dev = index->keyValue;
|
||||
pico_aodv_set_dev(dev);
|
||||
pico_socket_sendto_extended(aodv_socket, pkt, size, &all_bcast, short_be(PICO_AODV_PORT), info);
|
||||
pico_aodv_dbg("Forwarding %s: complete! ==== \n", reply ? "REPLY" : "REQUEST");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t aodv_lifetime(struct pico_aodv_node *node)
|
||||
{
|
||||
uint32_t lifetime;
|
||||
pico_time now = PICO_TIME_MS();
|
||||
if (!node->last_seen)
|
||||
node->last_seen = now;
|
||||
|
||||
if ((now - node->last_seen) > AODV_ACTIVE_ROUTE_TIMEOUT)
|
||||
return 0;
|
||||
|
||||
lifetime = AODV_ACTIVE_ROUTE_TIMEOUT - (uint32_t)(now - node->last_seen);
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
static void aodv_send_reply(struct pico_aodv_node *node, struct pico_aodv_rreq *req, int node_is_local, struct pico_msginfo *info)
|
||||
{
|
||||
struct pico_aodv_rrep reply;
|
||||
union pico_address dest;
|
||||
union pico_address oaddr;
|
||||
struct pico_aodv_node *orig;
|
||||
oaddr.ip4.addr = req->orig;
|
||||
orig = get_node_by_addr(&oaddr);
|
||||
reply.type = AODV_TYPE_RREP;
|
||||
reply.dest = req->dest;
|
||||
reply.dseq = req->dseq;
|
||||
reply.orig = req->orig;
|
||||
if (!orig)
|
||||
return;
|
||||
|
||||
reply.hop_count = (uint8_t)(orig->metric - 1u);
|
||||
|
||||
|
||||
dest.ip4.addr = 0xFFFFFFFF; /* wide broadcast */
|
||||
|
||||
if (short_be(req->req_flags) & AODV_RREQ_FLAG_G) {
|
||||
dest.ip4.addr = req->orig;
|
||||
} else {
|
||||
pico_aodv_set_dev(info->dev);
|
||||
}
|
||||
|
||||
if (node_is_local) {
|
||||
reply.lifetime = long_be(AODV_MY_ROUTE_TIMEOUT);
|
||||
reply.dseq = long_be(++pico_aodv_local_id);
|
||||
pico_socket_sendto(aodv_socket, &reply, sizeof(reply), &dest, short_be(PICO_AODV_PORT));
|
||||
} else if (((short_be(req->req_flags) & AODV_RREQ_FLAG_D) == 0) && (node->flags & PICO_AODV_NODE_SYNC)) {
|
||||
reply.lifetime = long_be(aodv_lifetime(node));
|
||||
reply.dseq = long_be(node->dseq);
|
||||
pico_aodv_dbg("Generating RREP for node %x, id=%x\n", reply.dest, reply.dseq);
|
||||
pico_socket_sendto(aodv_socket, &reply, sizeof(reply), &dest, short_be(PICO_AODV_PORT));
|
||||
}
|
||||
|
||||
pico_aodv_dbg("no rrep generated.\n");
|
||||
}
|
||||
|
||||
/* Parser functions */
|
||||
|
||||
static int aodv_send_req(struct pico_aodv_node *node);
|
||||
|
||||
static void aodv_reverse_path_discover(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_aodv_node *origin = (struct pico_aodv_node *)arg;
|
||||
(void)now;
|
||||
pico_aodv_dbg("Sending G RREQ to ORIGIN (metric = %d).\n", origin->metric);
|
||||
origin->ring_ttl = origin->metric;
|
||||
aodv_send_req(origin);
|
||||
}
|
||||
|
||||
static void aodv_recv_valid_rreq(struct pico_aodv_node *node, struct pico_aodv_rreq *req, struct pico_msginfo *info)
|
||||
{
|
||||
struct pico_device *dev;
|
||||
dev = pico_ipv4_link_find(&node->dest.ip4);
|
||||
pico_aodv_dbg("Valid req.\n");
|
||||
if (dev || PICO_AODV_ACTIVE(node)) {
|
||||
/* if destination is ourselves, or we have a possible route: Send reply. */
|
||||
aodv_send_reply(node, req, dev != NULL, info);
|
||||
if (dev) {
|
||||
/* if really for us, we need to build the return route. Initiate a gratuitous request. */
|
||||
union pico_address origin_addr;
|
||||
struct pico_aodv_node *origin;
|
||||
origin_addr.ip4.addr = req->orig;
|
||||
origin = get_node_by_addr(&origin_addr);
|
||||
if (origin) {
|
||||
origin->flags |= PICO_AODV_NODE_ROUTE_DOWN;
|
||||
if (!pico_timer_add(AODV_PATH_DISCOVERY_TIME, aodv_reverse_path_discover, origin)) {
|
||||
pico_aodv_dbg("AODV: Failed to start path discovery timer\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pico_aodv_dbg("Replied.\n");
|
||||
} else {
|
||||
/* destination unknown. Evaluate forwarding. */
|
||||
pico_aodv_dbg(" == Forwarding == .\n");
|
||||
aodv_forward(req, info, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void aodv_parse_rreq(union pico_address *from, uint8_t *buf, int len, struct pico_msginfo *msginfo)
|
||||
{
|
||||
struct pico_aodv_rreq *req = (struct pico_aodv_rreq *) buf;
|
||||
struct pico_aodv_node *node = NULL;
|
||||
struct pico_device *dev;
|
||||
union pico_address orig, dest;
|
||||
(void)from;
|
||||
if (len != (int)sizeof(struct pico_aodv_rreq))
|
||||
return;
|
||||
|
||||
orig.ip4.addr = req->orig;
|
||||
dev = pico_ipv4_link_find(&orig.ip4);
|
||||
if (dev) {
|
||||
pico_aodv_dbg("RREQ <-- myself\n");
|
||||
return;
|
||||
}
|
||||
|
||||
node = aodv_peer_eval(&orig, req->oseq, 1);
|
||||
if (!node) {
|
||||
pico_aodv_dbg("RREQ: Neighbor is not valid. oseq=%d\n", long_be(req->oseq));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->hop_count > 0)
|
||||
aodv_elect_route(node, from, req->hop_count, msginfo->dev);
|
||||
else
|
||||
aodv_elect_route(node, NULL, 0, msginfo->dev);
|
||||
|
||||
dest.ip4.addr = req->dest;
|
||||
node = aodv_peer_eval(&dest, req->dseq, !(req->req_flags & short_be(AODV_RREQ_FLAG_U)));
|
||||
if (!node) {
|
||||
node = aodv_peer_new(&dest);
|
||||
pico_aodv_dbg("RREQ: New peer! %08x\n", dest.ip4.addr);
|
||||
}
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
aodv_recv_valid_rreq(node, req, msginfo);
|
||||
}
|
||||
|
||||
static void aodv_parse_rrep(union pico_address *from, uint8_t *buf, int len, struct pico_msginfo *msginfo)
|
||||
{
|
||||
struct pico_aodv_rrep *rep = (struct pico_aodv_rrep *) buf;
|
||||
struct pico_aodv_node *node = NULL;
|
||||
union pico_address dest;
|
||||
union pico_address orig;
|
||||
struct pico_device *dev = NULL;
|
||||
if (len != (int)sizeof(struct pico_aodv_rrep))
|
||||
return;
|
||||
|
||||
dest.ip4.addr = rep->dest;
|
||||
orig.ip4.addr = rep->orig;
|
||||
dev = pico_ipv4_link_find(&dest.ip4);
|
||||
|
||||
if (dev) /* Our reply packet got rebounced, no useful information here, no need to fwd. */
|
||||
return;
|
||||
|
||||
pico_aodv_dbg("::::::::::::: Parsing RREP for node %08x\n", rep->dest);
|
||||
node = aodv_peer_eval(&dest, rep->dseq, 1);
|
||||
if (node) {
|
||||
pico_aodv_dbg("::::::::::::: Node found. Electing route and forwarding.\n");
|
||||
dest.ip4.addr = node->dest.ip4.addr;
|
||||
if (rep->hop_count > 0)
|
||||
aodv_elect_route(node, from, rep->hop_count, msginfo->dev);
|
||||
else
|
||||
aodv_elect_route(node, NULL, 0, msginfo->dev);
|
||||
|
||||
/* If we are the final destination for the reply (orig), no need to forward. */
|
||||
if (pico_ipv4_link_find(&orig.ip4)) {
|
||||
node->flags |= PICO_AODV_NODE_ROUTE_UP;
|
||||
} else {
|
||||
aodv_forward(rep, msginfo, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aodv_parse_rerr(union pico_address *from, uint8_t *buf, int len, struct pico_msginfo *msginfo)
|
||||
{
|
||||
if ((uint32_t)len < sizeof(struct pico_aodv_rerr) ||
|
||||
(((uint32_t)len - sizeof(struct pico_aodv_rerr)) % sizeof(struct pico_aodv_unreachable)) > 0)
|
||||
return;
|
||||
|
||||
(void)from;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
(void)msginfo;
|
||||
/* TODO: invalidate routes. This only makes sense if we are using HELLO messages. */
|
||||
}
|
||||
|
||||
static void aodv_parse_rack(union pico_address *from, uint8_t *buf, int len, struct pico_msginfo *msginfo)
|
||||
{
|
||||
if (len != (int)sizeof(struct pico_aodv_rack))
|
||||
return;
|
||||
|
||||
(void)from;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
(void)msginfo;
|
||||
}
|
||||
|
||||
struct aodv_parser_s {
|
||||
void (*call)(union pico_address *from, uint8_t *buf, int len, struct pico_msginfo *msginfo);
|
||||
};
|
||||
|
||||
static struct aodv_parser_s aodv_parser[5] = {
|
||||
{.call = NULL},
|
||||
{.call = aodv_parse_rreq },
|
||||
{.call = aodv_parse_rrep },
|
||||
{.call = aodv_parse_rerr },
|
||||
{.call = aodv_parse_rack }
|
||||
};
|
||||
|
||||
|
||||
static void pico_aodv_parse(union pico_address *from, uint8_t *buf, int len, struct pico_msginfo *msginfo)
|
||||
{
|
||||
struct pico_aodv_node *node;
|
||||
uint8_t hopcount = 0;
|
||||
if ((buf[0] < 1) || (buf[0] > 4)) {
|
||||
/* Type is invalid. Discard silently. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf[0] == AODV_TYPE_RREQ) {
|
||||
hopcount = ((struct pico_aodv_rreq *)buf)->hop_count;
|
||||
}
|
||||
|
||||
if (buf[0] == AODV_TYPE_RREP) {
|
||||
hopcount = ((struct pico_aodv_rrep *)buf)->hop_count;
|
||||
}
|
||||
|
||||
node = aodv_peer_eval(from, 0, 0);
|
||||
if (!node)
|
||||
node = aodv_peer_new(from);
|
||||
|
||||
if (node && (hopcount == 0)) {
|
||||
aodv_elect_route(node, NULL, hopcount, msginfo->dev);
|
||||
}
|
||||
|
||||
pico_aodv_dbg("Received AODV packet, ttl = %d\n", msginfo->ttl);
|
||||
aodv_parser[buf[0]].call(from, buf, len, msginfo);
|
||||
}
|
||||
|
||||
static void pico_aodv_socket_callback(uint16_t ev, struct pico_socket *s)
|
||||
{
|
||||
static uint8_t aodv_pkt[AODV_MAX_PKT];
|
||||
static union pico_address from;
|
||||
static struct pico_msginfo msginfo;
|
||||
uint16_t sport;
|
||||
int r;
|
||||
if (s != aodv_socket)
|
||||
return;
|
||||
|
||||
if (ev & PICO_SOCK_EV_RD) {
|
||||
r = pico_socket_recvfrom_extended(s, aodv_pkt, AODV_MAX_PKT, &from, &sport, &msginfo);
|
||||
if (r <= 0)
|
||||
return;
|
||||
|
||||
pico_aodv_dbg("Received AODV packet: %d bytes \n", r);
|
||||
|
||||
pico_aodv_parse(&from, aodv_pkt, r, &msginfo);
|
||||
}
|
||||
}
|
||||
|
||||
static void aodv_make_rreq(struct pico_aodv_node *node, struct pico_aodv_rreq *req)
|
||||
{
|
||||
memset(req, 0, sizeof(struct pico_aodv_rreq));
|
||||
req->type = AODV_TYPE_RREQ;
|
||||
|
||||
if (0 == (node->flags & PICO_AODV_NODE_SYNC)) {
|
||||
req->req_flags |= short_be(AODV_RREQ_FLAG_U); /* no known dseq, mark as U */
|
||||
req->dseq = 0; /* Unknown */
|
||||
} else {
|
||||
req->dseq = long_be(node->dseq);
|
||||
req->req_flags |= short_be(AODV_RREQ_FLAG_G); /* RFC3561 $6.3: we SHOULD set G flag as originators */
|
||||
}
|
||||
|
||||
/* Hop count = 0; */
|
||||
req->rreq_id = long_be(++pico_aodv_local_id);
|
||||
req->dest = node->dest.ip4.addr;
|
||||
req->oseq = long_be(pico_aodv_local_id);
|
||||
}
|
||||
|
||||
static void aodv_retrans_rreq(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_aodv_node *node = (struct pico_aodv_node *)arg;
|
||||
struct pico_device *dev;
|
||||
struct pico_tree_node *index;
|
||||
static struct pico_aodv_rreq rreq;
|
||||
struct pico_ipv4_link *ip4l = NULL;
|
||||
struct pico_msginfo info = {
|
||||
.dev = NULL, .tos = 0, .ttl = AODV_TTL_START
|
||||
};
|
||||
(void)now;
|
||||
|
||||
memset(&rreq, 0, sizeof(rreq));
|
||||
|
||||
if (node->flags & PICO_AODV_NODE_ROUTE_UP) {
|
||||
pico_aodv_dbg("------------------------------------------------------ Node %08x already active.\n", node->dest.ip4.addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->ring_ttl > AODV_TTL_THRESHOLD) {
|
||||
node->ring_ttl = AODV_NET_DIAMETER;
|
||||
pico_aodv_dbg("----------- DIAMETER reached.\n");
|
||||
}
|
||||
|
||||
|
||||
if (node->rreq_retry > AODV_RREQ_RETRIES) {
|
||||
node->rreq_retry = 0;
|
||||
node->ring_ttl = 0;
|
||||
pico_aodv_dbg("Node is unreachable.\n");
|
||||
node->flags &= (uint16_t)(~PICO_AODV_NODE_ROUTE_DOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->ring_ttl == AODV_NET_DIAMETER) {
|
||||
node->rreq_retry++;
|
||||
pico_aodv_dbg("Retry #%d\n", node->rreq_retry);
|
||||
}
|
||||
|
||||
aodv_make_rreq(node, &rreq);
|
||||
info.ttl = (uint8_t)node->ring_ttl;
|
||||
pico_tree_foreach(index, &aodv_devices){
|
||||
dev = index->keyValue;
|
||||
pico_aodv_set_dev(dev);
|
||||
ip4l = pico_ipv4_link_by_dev(dev);
|
||||
if (ip4l) {
|
||||
rreq.orig = ip4l->address.addr;
|
||||
pico_socket_sendto_extended(aodv_socket, &rreq, sizeof(rreq), &all_bcast, short_be(PICO_AODV_PORT), &info);
|
||||
}
|
||||
}
|
||||
if (node->ring_ttl < AODV_NET_DIAMETER)
|
||||
node->ring_ttl = (uint8_t)(node->ring_ttl + AODV_TTL_INCREMENT);
|
||||
|
||||
if (!pico_timer_add((pico_time)AODV_RING_TRAVERSAL_TIME(node->ring_ttl), aodv_retrans_rreq, node)) {
|
||||
pico_aodv_dbg("AODV: Failed to start retransmission timer\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int aodv_send_req(struct pico_aodv_node *node)
|
||||
{
|
||||
struct pico_device *dev;
|
||||
struct pico_tree_node *index;
|
||||
static struct pico_aodv_rreq rreq;
|
||||
int n = 0;
|
||||
struct pico_ipv4_link *ip4l = NULL;
|
||||
struct pico_msginfo info = {
|
||||
.dev = NULL, .tos = 0, .ttl = AODV_TTL_START
|
||||
};
|
||||
memset(&rreq, 0, sizeof(rreq));
|
||||
|
||||
if (PICO_AODV_ACTIVE(node))
|
||||
return 0;
|
||||
|
||||
node->flags |= PICO_AODV_NODE_REQUESTING;
|
||||
|
||||
if (pico_tree_empty(&aodv_devices))
|
||||
return n;
|
||||
|
||||
if (!aodv_socket) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->flags & PICO_AODV_NODE_ROUTE_DOWN) {
|
||||
info.ttl = node->metric;
|
||||
}
|
||||
|
||||
aodv_make_rreq(node, &rreq);
|
||||
pico_tree_foreach(index, &aodv_devices) {
|
||||
dev = index->keyValue;
|
||||
pico_aodv_set_dev(dev);
|
||||
ip4l = pico_ipv4_link_by_dev(dev);
|
||||
if (ip4l) {
|
||||
rreq.orig = ip4l->address.addr;
|
||||
pico_socket_sendto_extended(aodv_socket, &rreq, sizeof(rreq), &all_bcast, short_be(PICO_AODV_PORT), &info);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (!pico_timer_add((pico_time)AODV_RING_TRAVERSAL_TIME(1), aodv_retrans_rreq, node)) {
|
||||
pico_aodv_dbg("AODV: Failed to start retransmission timer\n");
|
||||
return -1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void pico_aodv_expired(struct pico_aodv_node *node)
|
||||
{
|
||||
node->flags |= PICO_AODV_NODE_UNREACH;
|
||||
node->flags &= (uint8_t)(~PICO_AODV_NODE_ROUTE_UP);
|
||||
node->flags &= (uint8_t)(~PICO_AODV_NODE_ROUTE_DOWN);
|
||||
pico_ipv4_route_del(node->dest.ip4, HOST_NETMASK, node->metric);
|
||||
node->ring_ttl = 0;
|
||||
/* TODO: send err */
|
||||
|
||||
}
|
||||
|
||||
static void pico_aodv_collector(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_tree_node *index;
|
||||
struct pico_aodv_node *node;
|
||||
(void)arg;
|
||||
(void)now;
|
||||
pico_tree_foreach(index, &aodv_nodes){
|
||||
node = index->keyValue;
|
||||
if (PICO_AODV_ACTIVE(node)) {
|
||||
uint32_t lifetime = aodv_lifetime(node);
|
||||
if (lifetime == 0)
|
||||
pico_aodv_expired(node);
|
||||
}
|
||||
}
|
||||
if (!pico_timer_add(AODV_HELLO_INTERVAL, pico_aodv_collector, NULL)) {
|
||||
pico_aodv_dbg("AODV: Failed to start collector timer\n");
|
||||
/* TODO what to do now? garbage collection will not be restarted, leading to memory leaks */
|
||||
}
|
||||
}
|
||||
|
||||
MOCKABLE int pico_aodv_init(void)
|
||||
{
|
||||
struct pico_ip4 any = {
|
||||
0
|
||||
};
|
||||
uint16_t port = short_be(PICO_AODV_PORT);
|
||||
if (aodv_socket) {
|
||||
pico_err = PICO_ERR_EADDRINUSE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
aodv_socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, pico_aodv_socket_callback);
|
||||
if (!aodv_socket)
|
||||
return -1;
|
||||
|
||||
if (pico_socket_bind(aodv_socket, &any, &port) != 0) {
|
||||
uint16_t err = pico_err;
|
||||
pico_socket_close(aodv_socket);
|
||||
pico_err = err;
|
||||
aodv_socket = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_aodv_local_id = pico_rand();
|
||||
if (!pico_timer_add(AODV_HELLO_INTERVAL, pico_aodv_collector, NULL)) {
|
||||
pico_aodv_dbg("AODV: Failed to start collector timer\n");
|
||||
pico_socket_close(aodv_socket);
|
||||
aodv_socket = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pico_aodv_add(struct pico_device *dev)
|
||||
{
|
||||
return (pico_tree_insert(&aodv_devices, dev)) ? (0) : (-1);
|
||||
}
|
||||
|
||||
void pico_aodv_refresh(const union pico_address *addr)
|
||||
{
|
||||
struct pico_aodv_node *node = get_node_by_addr(addr);
|
||||
if (node) {
|
||||
node->last_seen = PICO_TIME_MS();
|
||||
}
|
||||
}
|
||||
|
||||
int pico_aodv_lookup(const union pico_address *addr)
|
||||
{
|
||||
struct pico_aodv_node *node = get_node_by_addr(addr);
|
||||
if (!node)
|
||||
node = aodv_peer_new(addr);
|
||||
|
||||
if (!node)
|
||||
return -1;
|
||||
|
||||
if ((node->flags & PICO_AODV_NODE_ROUTE_UP) || (node->flags & PICO_AODV_NODE_ROUTE_DOWN))
|
||||
return 0;
|
||||
|
||||
if (node->ring_ttl < AODV_TTL_START) {
|
||||
node->ring_ttl = AODV_TTL_START;
|
||||
aodv_send_req(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int pico_aodv_init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pico_aodv_add(struct pico_device *dev)
|
||||
{
|
||||
(void)dev;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pico_aodv_lookup(const union pico_address *addr)
|
||||
{
|
||||
(void)addr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pico_aodv_refresh(const union pico_address *addr)
|
||||
{
|
||||
(void)addr;
|
||||
}
|
||||
|
||||
#endif
|
||||
130
kernel/picotcp/modules/pico_aodv.h
Normal file
130
kernel/picotcp/modules/pico_aodv.h
Normal file
@ -0,0 +1,130 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2015-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Author: Daniele Lacamera <daniele.lacamera@altran.com>
|
||||
*********************************************************************/
|
||||
#ifndef PICO_AODV_H_
|
||||
#define PICO_AODV_H_
|
||||
|
||||
/* RFC3561 */
|
||||
#define PICO_AODV_PORT (654)
|
||||
|
||||
/* RFC3561 $10 */
|
||||
#define AODV_ACTIVE_ROUTE_TIMEOUT (8000u) /* Conservative value for link breakage detection */
|
||||
#define AODV_DELETE_PERIOD (5 * AODV_ACTIVE_ROUTE_TIMEOUT) /* Recommended value K = 5 */
|
||||
#define AODV_ALLOWED_HELLO_LOSS (4) /* conservative */
|
||||
#define AODV_NET_DIAMETER ((uint8_t)(35))
|
||||
#define AODV_RREQ_RETRIES (2)
|
||||
#define AODV_NODE_TRAVERSAL_TIME (40)
|
||||
#define AODV_HELLO_INTERVAL (1000)
|
||||
#define AODV_LOCAL_ADD_TTL 2
|
||||
#define AODV_RREQ_RATELIMIT (10)
|
||||
#define AODV_TIMEOUT_BUFFER (2)
|
||||
#define AODV_TTL_START ((uint8_t)(1))
|
||||
#define AODV_TTL_INCREMENT 2
|
||||
#define AODV_TTL_THRESHOLD ((uint8_t)(7))
|
||||
#define AODV_RERR_RATELIMIT (10)
|
||||
#define AODV_MAX_REPAIR_TTL ((uint8_t)(AODV_NET_DIAMETER / 3))
|
||||
#define AODV_MY_ROUTE_TIMEOUT (2 * AODV_ACTIVE_ROUTE_TIMEOUT)
|
||||
#define AODV_NET_TRAVERSAL_TIME (2 * AODV_NODE_TRAVERSAL_TIME * AODV_NET_DIAMETER)
|
||||
#define AODV_BLACKLIST_TIMEOUT (AODV_RREQ_RETRIES * AODV_NET_TRAVERSAL_TIME)
|
||||
#define AODV_NEXT_HOP_WAIT (AODV_NODE_TRAVERSAL_TIME + 10)
|
||||
#define AODV_PATH_DISCOVERY_TIME (2 * AODV_NET_TRAVERSAL_TIME)
|
||||
#define AODV_RING_TRAVERSAL_TIME(ttl) (2 * AODV_NODE_TRAVERSAL_TIME * (ttl + AODV_TIMEOUT_BUFFER))
|
||||
/* End section RFC3561 $10 */
|
||||
|
||||
|
||||
#define AODV_TYPE_RREQ 1
|
||||
#define AODV_TYPE_RREP 2
|
||||
#define AODV_TYPE_RERR 3
|
||||
#define AODV_TYPE_RACK 4
|
||||
|
||||
PACKED_STRUCT_DEF pico_aodv_rreq
|
||||
{
|
||||
uint8_t type;
|
||||
uint16_t req_flags;
|
||||
uint8_t hop_count;
|
||||
uint32_t rreq_id;
|
||||
uint32_t dest;
|
||||
uint32_t dseq;
|
||||
uint32_t orig;
|
||||
uint32_t oseq;
|
||||
};
|
||||
|
||||
#define AODV_RREQ_FLAG_J 0x8000
|
||||
#define AODV_RREQ_FLAG_R 0x4000
|
||||
#define AODV_RREQ_FLAG_G 0x2000
|
||||
#define AODV_RREQ_FLAG_D 0x1000
|
||||
#define AODV_RREQ_FLAG_U 0x0800
|
||||
#define AODV_RREQ_FLAG_RESERVED 0x07FF
|
||||
|
||||
PACKED_STRUCT_DEF pico_aodv_rrep
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t rep_flags;
|
||||
uint8_t prefix_sz;
|
||||
uint8_t hop_count;
|
||||
uint32_t dest;
|
||||
uint32_t dseq;
|
||||
uint32_t orig;
|
||||
uint32_t lifetime;
|
||||
};
|
||||
|
||||
#define AODV_RREP_MAX_PREFIX 0x1F
|
||||
#define AODV_RREP_FLAG_R 0x80
|
||||
#define AODV_RREP_FLAG_A 0x40
|
||||
#define AODV_RREP_FLAG_RESERVED 0x3F
|
||||
|
||||
#define PICO_AODV_NODE_NEW 0x0000
|
||||
#define PICO_AODV_NODE_SYNC 0x0001
|
||||
#define PICO_AODV_NODE_REQUESTING 0x0002
|
||||
#define PICO_AODV_NODE_ROUTE_UP 0x0004
|
||||
#define PICO_AODV_NODE_ROUTE_DOWN 0x0008
|
||||
#define PICO_AODV_NODE_IDLING 0x0010
|
||||
#define PICO_AODV_NODE_UNREACH 0x0020
|
||||
|
||||
#define PICO_AODV_ACTIVE(node) ((node->flags & PICO_AODV_NODE_ROUTE_UP) && (node->flags & PICO_AODV_NODE_ROUTE_DOWN))
|
||||
|
||||
|
||||
struct pico_aodv_node
|
||||
{
|
||||
union pico_address dest;
|
||||
pico_time last_seen;
|
||||
pico_time fwd_time;
|
||||
uint32_t dseq;
|
||||
uint16_t flags;
|
||||
uint8_t metric;
|
||||
uint8_t ring_ttl;
|
||||
uint8_t rreq_retry;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_aodv_unreachable
|
||||
{
|
||||
uint32_t addr;
|
||||
uint32_t dseq;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_aodv_rerr
|
||||
{
|
||||
uint8_t type;
|
||||
uint16_t rerr_flags;
|
||||
uint8_t dst_count;
|
||||
uint32_t unreach_addr;
|
||||
uint32_t unreach_dseq;
|
||||
struct pico_aodv_unreachable unreach[1]; /* unrechable nodes: must be at least 1. See dst_count field above */
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_aodv_rack
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t reserved;
|
||||
};
|
||||
|
||||
int pico_aodv_init(void);
|
||||
int pico_aodv_add(struct pico_device *dev);
|
||||
int pico_aodv_lookup(const union pico_address *addr);
|
||||
void pico_aodv_refresh(const union pico_address *addr);
|
||||
#endif
|
||||
566
kernel/picotcp/modules/pico_arp.c
Normal file
566
kernel/picotcp/modules/pico_arp.c
Normal file
@ -0,0 +1,566 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_arp.h"
|
||||
#include "pico_tree.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_ethernet.h"
|
||||
|
||||
extern const uint8_t PICO_ETHADDR_ALL[6];
|
||||
#define PICO_ARP_TIMEOUT 600000llu
|
||||
#define PICO_ARP_RETRY 300lu
|
||||
#define PICO_ARP_MAX_PENDING 5
|
||||
|
||||
#ifdef DEBUG_ARP
|
||||
#define arp_dbg dbg
|
||||
#else
|
||||
#define arp_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
static int max_arp_reqs = PICO_ARP_MAX_RATE;
|
||||
static struct pico_frame *frames_queued[PICO_ARP_MAX_PENDING] = {
|
||||
0
|
||||
};
|
||||
|
||||
static void pico_arp_queued_trigger(void)
|
||||
{
|
||||
int i;
|
||||
struct pico_frame *f;
|
||||
for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
|
||||
{
|
||||
f = frames_queued[i];
|
||||
if (f) {
|
||||
if (pico_datalink_send(f) <= 0)
|
||||
pico_frame_discard(f);
|
||||
frames_queued[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_max_arp_reqs(pico_time now, void *unused)
|
||||
{
|
||||
IGNORE_PARAMETER(now);
|
||||
IGNORE_PARAMETER(unused);
|
||||
if (max_arp_reqs < PICO_ARP_MAX_RATE)
|
||||
max_arp_reqs++;
|
||||
|
||||
if (!pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL)) {
|
||||
arp_dbg("ARP: Failed to start update_max_arps timer\n");
|
||||
/* TODO if this fails all incoming arps will be discarded once max_arp_reqs recahes 0 */
|
||||
}
|
||||
}
|
||||
|
||||
void pico_arp_init(void)
|
||||
{
|
||||
if (!pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL)) {
|
||||
arp_dbg("ARP: Failed to start update_max_arps timer\n");
|
||||
}
|
||||
}
|
||||
|
||||
PACKED_STRUCT_DEF pico_arp_hdr
|
||||
{
|
||||
uint16_t htype;
|
||||
uint16_t ptype;
|
||||
uint8_t hsize;
|
||||
uint8_t psize;
|
||||
uint16_t opcode;
|
||||
uint8_t s_mac[PICO_SIZE_ETH];
|
||||
struct pico_ip4 src;
|
||||
uint8_t d_mac[PICO_SIZE_ETH];
|
||||
struct pico_ip4 dst;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Callback handler for ip conflict service (e.g. IPv4 SLAAC)
|
||||
* Whenever the IP address registered here is seen in the network,
|
||||
* the callback is awaken to take countermeasures against IP collisions.
|
||||
*
|
||||
*/
|
||||
|
||||
struct arp_service_ipconflict {
|
||||
struct pico_eth mac;
|
||||
struct pico_ip4 ip;
|
||||
void (*conflict)(int);
|
||||
};
|
||||
|
||||
static struct arp_service_ipconflict conflict_ipv4;
|
||||
|
||||
|
||||
|
||||
#define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr)))
|
||||
|
||||
/* Arp Entries for the tables. */
|
||||
struct pico_arp {
|
||||
/* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure,
|
||||
* due to in-place casting!!! */
|
||||
struct pico_eth eth;
|
||||
struct pico_ip4 ipv4;
|
||||
int arp_status;
|
||||
pico_time timestamp;
|
||||
struct pico_device *dev;
|
||||
uint32_t timer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*****************/
|
||||
/** ARP TREE **/
|
||||
/*****************/
|
||||
|
||||
/* Routing destination */
|
||||
|
||||
static int arp_compare(void *ka, void *kb)
|
||||
{
|
||||
struct pico_arp *a = ka, *b = kb;
|
||||
return pico_ipv4_compare(&a->ipv4, &b->ipv4);
|
||||
}
|
||||
|
||||
static PICO_TREE_DECLARE(arp_tree, arp_compare);
|
||||
|
||||
/*********************/
|
||||
/** END ARP TREE **/
|
||||
/*********************/
|
||||
|
||||
struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst)
|
||||
{
|
||||
struct pico_arp search, *found;
|
||||
search.ipv4.addr = dst->addr;
|
||||
found = pico_tree_findKey(&arp_tree, &search);
|
||||
if (found && (found->arp_status != PICO_ARP_STATUS_STALE))
|
||||
return &found->eth;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst)
|
||||
{
|
||||
struct pico_arp*search;
|
||||
struct pico_tree_node *index;
|
||||
pico_tree_foreach(index, &arp_tree){
|
||||
search = index->keyValue;
|
||||
if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0)
|
||||
return &search->ipv4;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void pico_arp_unreachable(struct pico_ip4 *a)
|
||||
{
|
||||
int i;
|
||||
struct pico_frame *f;
|
||||
struct pico_ipv4_hdr *hdr;
|
||||
struct pico_ip4 dst;
|
||||
for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
|
||||
{
|
||||
f = frames_queued[i];
|
||||
if (f) {
|
||||
hdr = (struct pico_ipv4_hdr *) f->net_hdr;
|
||||
dst = pico_ipv4_route_get_gateway(&hdr->dst);
|
||||
if (!dst.addr)
|
||||
dst.addr = hdr->dst.addr;
|
||||
|
||||
if (dst.addr == a->addr) {
|
||||
if (!pico_source_is_local(f)) {
|
||||
pico_notify_dest_unreachable(f);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pico_arp_retry(struct pico_frame *f, struct pico_ip4 *where)
|
||||
{
|
||||
if (++f->failure_count < 4) {
|
||||
arp_dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
|
||||
/* check if dst is local (gateway = 0), or if to use gateway */
|
||||
pico_arp_request(f->dev, where, PICO_ARP_QUERY);
|
||||
} else {
|
||||
pico_arp_unreachable(where);
|
||||
}
|
||||
}
|
||||
|
||||
struct pico_eth *pico_arp_get(struct pico_frame *f)
|
||||
{
|
||||
struct pico_eth *a4;
|
||||
struct pico_ip4 gateway;
|
||||
struct pico_ip4 *where;
|
||||
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
|
||||
struct pico_ipv4_link *l;
|
||||
if (!hdr)
|
||||
return NULL;
|
||||
|
||||
l = pico_ipv4_link_get(&hdr->dst);
|
||||
if(l) {
|
||||
/* address belongs to ourself */
|
||||
return &l->dev->eth->mac;
|
||||
}
|
||||
|
||||
gateway = pico_ipv4_route_get_gateway(&hdr->dst);
|
||||
/* check if dst is local (gateway = 0), or if to use gateway */
|
||||
if (gateway.addr != 0)
|
||||
where = &gateway;
|
||||
else
|
||||
where = &hdr->dst;
|
||||
|
||||
a4 = pico_arp_lookup(where); /* check if dst ip mac in cache */
|
||||
|
||||
if (!a4)
|
||||
pico_arp_retry(f, where);
|
||||
|
||||
return a4;
|
||||
}
|
||||
|
||||
|
||||
void pico_arp_postpone(struct pico_frame *f)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
|
||||
{
|
||||
if (!frames_queued[i]) {
|
||||
if (f->failure_count < 4)
|
||||
frames_queued[i] = f;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Not possible to enqueue: caller will discard packet */
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_ARP
|
||||
static void dbg_arp(void)
|
||||
{
|
||||
struct pico_arp *a;
|
||||
struct pico_tree_node *index;
|
||||
|
||||
pico_tree_foreach(index, &arp_tree) {
|
||||
a = index->keyValue;
|
||||
arp_dbg("ARP to %08x, mac: %02x:%02x:%02x:%02x:%02x:%02x\n", a->ipv4.addr, a->eth.addr[0], a->eth.addr[1], a->eth.addr[2], a->eth.addr[3], a->eth.addr[4], a->eth.addr[5] );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void arp_expire(pico_time now, void *_stale)
|
||||
{
|
||||
struct pico_arp *stale = (struct pico_arp *) _stale;
|
||||
if (now >= (stale->timestamp + PICO_ARP_TIMEOUT)) {
|
||||
stale->arp_status = PICO_ARP_STATUS_STALE;
|
||||
arp_dbg("ARP: Setting arp_status to STALE\n");
|
||||
pico_arp_request(stale->dev, &stale->ipv4, PICO_ARP_QUERY);
|
||||
} else {
|
||||
/* Timer must be rescheduled, ARP entry has been renewed lately.
|
||||
* No action required to refresh the entry, will check on the next timeout */
|
||||
if (!pico_timer_add(PICO_ARP_TIMEOUT + stale->timestamp - now, arp_expire, stale)) {
|
||||
arp_dbg("ARP: Failed to start expiration timer, destroying arp entry\n");
|
||||
pico_tree_delete(&arp_tree, stale);
|
||||
PICO_FREE(stale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pico_arp_add_entry(struct pico_arp *entry)
|
||||
{
|
||||
entry->arp_status = PICO_ARP_STATUS_REACHABLE;
|
||||
entry->timestamp = PICO_TIME();
|
||||
|
||||
if (pico_tree_insert(&arp_tree, entry)) {
|
||||
arp_dbg("ARP: Failed to insert new entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
arp_dbg("ARP ## reachable.\n");
|
||||
pico_arp_queued_trigger();
|
||||
if (!pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry)) {
|
||||
arp_dbg("ARP: Failed to start expiration timer\n");
|
||||
pico_tree_delete(&arp_tree, entry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_arp_create_entry(uint8_t *hwaddr, struct pico_ip4 ipv4, struct pico_device *dev)
|
||||
{
|
||||
struct pico_arp*arp = PICO_ZALLOC(sizeof(struct pico_arp));
|
||||
if(!arp) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(arp->eth.addr, hwaddr, 6);
|
||||
arp->ipv4.addr = ipv4.addr;
|
||||
arp->dev = dev;
|
||||
|
||||
if (pico_arp_add_entry(arp) < 0) {
|
||||
PICO_FREE(arp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pico_arp_check_conflict(struct pico_arp_hdr *hdr)
|
||||
{
|
||||
if (conflict_ipv4.conflict)
|
||||
{
|
||||
if((conflict_ipv4.ip.addr == hdr->src.addr) &&
|
||||
(memcmp(hdr->s_mac, conflict_ipv4.mac.addr, PICO_SIZE_ETH) != 0))
|
||||
conflict_ipv4.conflict(PICO_ARP_CONFLICT_REASON_CONFLICT );
|
||||
|
||||
if((hdr->src.addr == 0) && (hdr->dst.addr == conflict_ipv4.ip.addr))
|
||||
conflict_ipv4.conflict(PICO_ARP_CONFLICT_REASON_PROBE );
|
||||
}
|
||||
}
|
||||
|
||||
static struct pico_arp *pico_arp_lookup_entry(struct pico_frame *f)
|
||||
{
|
||||
struct pico_arp search;
|
||||
struct pico_arp *found = NULL;
|
||||
struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr;
|
||||
/* Populate a new arp entry */
|
||||
search.ipv4.addr = hdr->src.addr;
|
||||
|
||||
/* Search for already existing entry */
|
||||
found = pico_tree_findKey(&arp_tree, &search);
|
||||
if (found) {
|
||||
if (found->arp_status == PICO_ARP_STATUS_STALE) {
|
||||
/* Replace if stale */
|
||||
pico_tree_delete(&arp_tree, found);
|
||||
if (pico_arp_add_entry(found) < 0) {
|
||||
arp_dbg("ARP: Failed to re-instert stale arp entry\n");
|
||||
PICO_FREE(found);
|
||||
found = NULL;
|
||||
}
|
||||
} else {
|
||||
/* Update mac address */
|
||||
memcpy(found->eth.addr, hdr->s_mac, PICO_SIZE_ETH);
|
||||
arp_dbg("ARP entry updated!\n");
|
||||
|
||||
/* Refresh timestamp, this will force a reschedule on the next timeout*/
|
||||
found->timestamp = PICO_TIME();
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
static int pico_arp_check_incoming_hdr_type(struct pico_arp_hdr *h)
|
||||
{
|
||||
/* Check the hardware type and protocol */
|
||||
if ((h->htype != PICO_ARP_HTYPE_ETH) || (h->ptype != PICO_IDETH_IPV4))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_arp_check_incoming_hdr(struct pico_frame *f, struct pico_ip4 *dst_addr)
|
||||
{
|
||||
struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr;
|
||||
if (!hdr)
|
||||
return -1;
|
||||
|
||||
dst_addr->addr = hdr->dst.addr;
|
||||
if (pico_arp_check_incoming_hdr_type(hdr) < 0)
|
||||
return -1;
|
||||
|
||||
/* The source mac address must not be a multicast or broadcast address */
|
||||
if (hdr->s_mac[0] & 0x01)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pico_arp_reply_on_request(struct pico_frame *f, struct pico_ip4 me)
|
||||
{
|
||||
struct pico_arp_hdr *hdr;
|
||||
struct pico_eth_hdr *eh;
|
||||
|
||||
hdr = (struct pico_arp_hdr *) f->net_hdr;
|
||||
eh = (struct pico_eth_hdr *)f->datalink_hdr;
|
||||
if (hdr->opcode != PICO_ARP_REQUEST)
|
||||
return;
|
||||
|
||||
hdr->opcode = PICO_ARP_REPLY;
|
||||
memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH);
|
||||
memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
hdr->dst.addr = hdr->src.addr;
|
||||
hdr->src.addr = me.addr;
|
||||
|
||||
/* Prepare eth header for arp reply */
|
||||
memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH);
|
||||
memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
f->start = f->datalink_hdr;
|
||||
f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR;
|
||||
f->dev->send(f->dev, f->start, (int)f->len);
|
||||
}
|
||||
|
||||
static int pico_arp_check_flooding(struct pico_frame *f, struct pico_ip4 me)
|
||||
{
|
||||
struct pico_device *link_dev;
|
||||
struct pico_arp_hdr *hdr;
|
||||
hdr = (struct pico_arp_hdr *) f->net_hdr;
|
||||
|
||||
/* Prevent ARP flooding */
|
||||
link_dev = pico_ipv4_link_find(&me);
|
||||
if ((link_dev == f->dev) && (hdr->opcode == PICO_ARP_REQUEST)) {
|
||||
if (max_arp_reqs == 0)
|
||||
return -1;
|
||||
else
|
||||
max_arp_reqs--;
|
||||
}
|
||||
|
||||
/* Check if we are the target IP address */
|
||||
if (link_dev != f->dev)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_arp_process_in(struct pico_frame *f, struct pico_arp_hdr *hdr, struct pico_arp *found)
|
||||
{
|
||||
struct pico_ip4 me;
|
||||
if (pico_arp_check_incoming_hdr(f, &me) < 0) {
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_arp_check_flooding(f, me) < 0) {
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If no existing entry was found, create a new entry, or fail trying. */
|
||||
if ((!found) && (pico_arp_create_entry(hdr->s_mac, hdr->src, f->dev) < 0)) {
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If the packet is a request, send a reply */
|
||||
pico_arp_reply_on_request(f, me);
|
||||
|
||||
#ifdef DEBUG_ARP
|
||||
dbg_arp();
|
||||
#endif
|
||||
pico_frame_discard(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_arp_receive(struct pico_frame *f)
|
||||
{
|
||||
struct pico_arp_hdr *hdr;
|
||||
struct pico_arp *found = NULL;
|
||||
|
||||
hdr = (struct pico_arp_hdr *) f->net_hdr;
|
||||
if (!hdr) {
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_arp_check_conflict(hdr);
|
||||
found = pico_arp_lookup_entry(f);
|
||||
return pico_arp_process_in(f, hdr, found);
|
||||
|
||||
}
|
||||
|
||||
static int32_t pico_arp_request_xmit(struct pico_device *dev, struct pico_frame *f, struct pico_ip4 *src, struct pico_ip4 *dst, uint8_t type)
|
||||
{
|
||||
struct pico_arp_hdr *ah = (struct pico_arp_hdr *) (f->start + PICO_SIZE_ETHHDR);
|
||||
int ret;
|
||||
|
||||
/* Fill arp header */
|
||||
ah->htype = PICO_ARP_HTYPE_ETH;
|
||||
ah->ptype = PICO_IDETH_IPV4;
|
||||
ah->hsize = PICO_SIZE_ETH;
|
||||
ah->psize = PICO_SIZE_IP4;
|
||||
ah->opcode = PICO_ARP_REQUEST;
|
||||
memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
|
||||
switch (type) {
|
||||
case PICO_ARP_ANNOUNCE:
|
||||
ah->src.addr = dst->addr;
|
||||
ah->dst.addr = dst->addr;
|
||||
break;
|
||||
case PICO_ARP_PROBE:
|
||||
ah->src.addr = 0;
|
||||
ah->dst.addr = dst->addr;
|
||||
break;
|
||||
case PICO_ARP_QUERY:
|
||||
ah->src.addr = src->addr;
|
||||
ah->dst.addr = dst->addr;
|
||||
break;
|
||||
default:
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
arp_dbg("Sending arp request.\n");
|
||||
ret = dev->send(dev, f->start, (int) f->len);
|
||||
pico_frame_discard(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t pico_arp_request(struct pico_device *dev, struct pico_ip4 *dst, uint8_t type)
|
||||
{
|
||||
struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR);
|
||||
struct pico_eth_hdr *eh;
|
||||
struct pico_ip4 *src = NULL;
|
||||
|
||||
if (!q)
|
||||
return -1;
|
||||
|
||||
if (type == PICO_ARP_QUERY)
|
||||
{
|
||||
src = pico_ipv4_source_find(dst);
|
||||
if (!src) {
|
||||
pico_frame_discard(q);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
arp_dbg("QUERY: %08x\n", dst->addr);
|
||||
|
||||
eh = (struct pico_eth_hdr *)q->start;
|
||||
|
||||
/* Fill eth header */
|
||||
memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
|
||||
eh->proto = PICO_IDETH_ARP;
|
||||
|
||||
return pico_arp_request_xmit(dev, q, src, dst, type);
|
||||
}
|
||||
|
||||
int pico_arp_get_neighbors(struct pico_device *dev, struct pico_ip4 *neighbors, int maxlen)
|
||||
{
|
||||
struct pico_arp*search;
|
||||
struct pico_tree_node *index;
|
||||
int i = 0;
|
||||
pico_tree_foreach(index, &arp_tree){
|
||||
search = index->keyValue;
|
||||
if (search->dev == dev) {
|
||||
neighbors[i++].addr = search->ipv4.addr;
|
||||
if (i >= maxlen)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void pico_arp_register_ipconflict(struct pico_ip4 *ip, struct pico_eth *mac, void (*cb)(int reason))
|
||||
{
|
||||
conflict_ipv4.conflict = cb;
|
||||
conflict_ipv4.ip.addr = ip->addr;
|
||||
if (mac != NULL)
|
||||
memcpy(conflict_ipv4.mac.addr, mac, 6);
|
||||
}
|
||||
|
||||
35
kernel/picotcp/modules/pico_arp.h
Normal file
35
kernel/picotcp/modules/pico_arp.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_ARP
|
||||
#define INCLUDE_PICO_ARP
|
||||
#include "pico_eth.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
int pico_arp_receive(struct pico_frame *);
|
||||
|
||||
|
||||
struct pico_eth *pico_arp_get(struct pico_frame *f);
|
||||
int32_t pico_arp_request(struct pico_device *dev, struct pico_ip4 *dst, uint8_t type);
|
||||
|
||||
#define PICO_ARP_STATUS_REACHABLE 0x00
|
||||
#define PICO_ARP_STATUS_PERMANENT 0x01
|
||||
#define PICO_ARP_STATUS_STALE 0x02
|
||||
|
||||
#define PICO_ARP_QUERY 0x00
|
||||
#define PICO_ARP_PROBE 0x01
|
||||
#define PICO_ARP_ANNOUNCE 0x02
|
||||
|
||||
#define PICO_ARP_CONFLICT_REASON_CONFLICT 0
|
||||
#define PICO_ARP_CONFLICT_REASON_PROBE 1
|
||||
|
||||
struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst);
|
||||
struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst);
|
||||
int pico_arp_create_entry(uint8_t*hwaddr, struct pico_ip4 ipv4, struct pico_device*dev);
|
||||
int pico_arp_get_neighbors(struct pico_device *dev, struct pico_ip4 *neighbors, int maxlen);
|
||||
void pico_arp_register_ipconflict(struct pico_ip4 *ip, struct pico_eth *mac, void (*cb)(int reason));
|
||||
void pico_arp_postpone(struct pico_frame *f);
|
||||
void pico_arp_init(void);
|
||||
#endif
|
||||
109
kernel/picotcp/modules/pico_dev_ipc.c
Normal file
109
kernel/picotcp/modules/pico_dev_ipc.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Michiel Kustermans
|
||||
*********************************************************************/
|
||||
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_ipc.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
struct pico_device_ipc {
|
||||
struct pico_device dev;
|
||||
int fd;
|
||||
};
|
||||
|
||||
#define IPC_MTU 2048
|
||||
|
||||
static int pico_ipc_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
struct pico_device_ipc *ipc = (struct pico_device_ipc *) dev;
|
||||
return (int)write(ipc->fd, buf, (uint32_t)len);
|
||||
}
|
||||
|
||||
static int pico_ipc_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct pico_device_ipc *ipc = (struct pico_device_ipc *) dev;
|
||||
struct pollfd pfd;
|
||||
unsigned char buf[IPC_MTU];
|
||||
int len;
|
||||
pfd.fd = ipc->fd;
|
||||
pfd.events = POLLIN;
|
||||
do {
|
||||
if (poll(&pfd, 1, 0) <= 0)
|
||||
return loop_score;
|
||||
|
||||
len = (int)read(ipc->fd, buf, IPC_MTU);
|
||||
if (len > 0) {
|
||||
loop_score--;
|
||||
pico_stack_recv(dev, buf, (uint32_t)len);
|
||||
}
|
||||
} while(loop_score > 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Public interface: create/destroy. */
|
||||
|
||||
void pico_ipc_destroy(struct pico_device *dev)
|
||||
{
|
||||
struct pico_device_ipc *ipc = (struct pico_device_ipc *) dev;
|
||||
if(ipc->fd > 0) {
|
||||
close(ipc->fd);
|
||||
}
|
||||
}
|
||||
|
||||
static int ipc_connect(const char *sock_path)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int ipc_fd;
|
||||
|
||||
if((ipc_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0) {
|
||||
return(-1);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
||||
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
|
||||
|
||||
if(connect(ipc_fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return ipc_fd;
|
||||
}
|
||||
|
||||
struct pico_device *pico_ipc_create(const char *sock_path, const char *name, const uint8_t *mac)
|
||||
{
|
||||
struct pico_device_ipc *ipc = PICO_ZALLOC(sizeof(struct pico_device_ipc));
|
||||
|
||||
if (!ipc)
|
||||
return NULL;
|
||||
|
||||
ipc->dev.mtu = IPC_MTU;
|
||||
|
||||
if( 0 != pico_device_init((struct pico_device *)ipc, name, mac)) {
|
||||
dbg("Ipc init failed.\n");
|
||||
pico_ipc_destroy((struct pico_device *)ipc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ipc->dev.overhead = 0;
|
||||
ipc->fd = ipc_connect(sock_path);
|
||||
if (ipc->fd < 0) {
|
||||
dbg("Ipc creation failed.\n");
|
||||
pico_ipc_destroy((struct pico_device *)ipc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ipc->dev.send = pico_ipc_send;
|
||||
ipc->dev.poll = pico_ipc_poll;
|
||||
ipc->dev.destroy = pico_ipc_destroy;
|
||||
dbg("Device %s created.\n", ipc->dev.name);
|
||||
return (struct pico_device *)ipc;
|
||||
}
|
||||
15
kernel/picotcp/modules/pico_dev_ipc.h
Normal file
15
kernel/picotcp/modules/pico_dev_ipc.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_IPC
|
||||
#define INCLUDE_PICO_IPC
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
void pico_ipc_destroy(struct pico_device *ipc);
|
||||
struct pico_device *pico_ipc_create(const char *sock_path, const char *name, const uint8_t *mac);
|
||||
|
||||
#endif
|
||||
|
||||
66
kernel/picotcp/modules/pico_dev_loop.c
Normal file
66
kernel/picotcp/modules/pico_dev_loop.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_loop.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
|
||||
#define LOOP_MTU 1500
|
||||
static uint8_t l_buf[LOOP_MTU];
|
||||
static int l_bufsize = 0;
|
||||
|
||||
|
||||
static int pico_loop_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
IGNORE_PARAMETER(dev);
|
||||
if (len > LOOP_MTU)
|
||||
return 0;
|
||||
|
||||
if (l_bufsize == 0) {
|
||||
memcpy(l_buf, buf, (size_t)len);
|
||||
l_bufsize += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_loop_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
if (loop_score <= 0)
|
||||
return 0;
|
||||
|
||||
if (l_bufsize > 0) {
|
||||
pico_stack_recv(dev, l_buf, (uint32_t)l_bufsize);
|
||||
l_bufsize = 0;
|
||||
loop_score--;
|
||||
}
|
||||
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
|
||||
struct pico_device *pico_loop_create(void)
|
||||
{
|
||||
struct pico_device *loop = PICO_ZALLOC(sizeof(struct pico_device));
|
||||
if (!loop)
|
||||
return NULL;
|
||||
|
||||
if( 0 != pico_device_init(loop, "loop", NULL)) {
|
||||
dbg ("Loop init failed.\n");
|
||||
pico_device_destroy(loop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
loop->send = pico_loop_send;
|
||||
loop->poll = pico_loop_poll;
|
||||
dbg("Device %s created.\n", loop->name);
|
||||
return loop;
|
||||
}
|
||||
|
||||
15
kernel/picotcp/modules/pico_dev_loop.h
Normal file
15
kernel/picotcp/modules/pico_dev_loop.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_LOOP
|
||||
#define INCLUDE_PICO_LOOP
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
void pico_loop_destroy(struct pico_device *loop);
|
||||
struct pico_device *pico_loop_create(void);
|
||||
|
||||
#endif
|
||||
|
||||
316
kernel/picotcp/modules/pico_dev_mock.c
Normal file
316
kernel/picotcp/modules/pico_dev_mock.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Frederik Van Slycken
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_mock.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_tree.h"
|
||||
|
||||
#define MOCK_MTU 1500
|
||||
|
||||
|
||||
|
||||
/* Tree for finding mock_device based on pico_device* */
|
||||
|
||||
|
||||
static int mock_dev_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct mock_device *a = ka, *b = kb;
|
||||
if (a->dev < b->dev)
|
||||
return -1;
|
||||
|
||||
if (a->dev > b->dev)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PICO_TREE_DECLARE(mock_device_tree, mock_dev_cmp);
|
||||
|
||||
static int pico_mock_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
struct mock_device search = {
|
||||
.dev = dev
|
||||
};
|
||||
struct mock_device*mock = pico_tree_findKey(&mock_device_tree, &search);
|
||||
struct mock_frame*frame;
|
||||
|
||||
if(!mock)
|
||||
return 0;
|
||||
|
||||
if (len > MOCK_MTU)
|
||||
return 0;
|
||||
|
||||
frame = PICO_ZALLOC(sizeof(struct mock_frame));
|
||||
if(!frame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(mock->out_head == NULL)
|
||||
mock->out_head = frame;
|
||||
else
|
||||
mock->out_tail->next = frame;
|
||||
|
||||
mock->out_tail = frame;
|
||||
|
||||
mock->out_tail->buffer = PICO_ZALLOC((uint32_t)len);
|
||||
if(!mock->out_tail->buffer)
|
||||
return 0;
|
||||
|
||||
memcpy(mock->out_tail->buffer, buf, (uint32_t)len);
|
||||
mock->out_tail->len = len;
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
static int pico_mock_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct mock_device search = {
|
||||
.dev = dev
|
||||
};
|
||||
struct mock_device*mock = pico_tree_findKey(&mock_device_tree, &search);
|
||||
struct mock_frame*nxt;
|
||||
|
||||
if(!mock)
|
||||
return 0;
|
||||
|
||||
if (loop_score <= 0)
|
||||
return 0;
|
||||
|
||||
while(mock->in_head != NULL && loop_score > 0)
|
||||
{
|
||||
pico_stack_recv(dev, mock->in_head->buffer, (uint32_t)mock->in_head->len);
|
||||
loop_score--;
|
||||
|
||||
PICO_FREE(mock->in_head->buffer);
|
||||
|
||||
if(mock->in_tail == mock->in_head) {
|
||||
PICO_FREE(mock->in_head);
|
||||
mock->in_tail = mock->in_head = NULL;
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
nxt = mock->in_head->next;
|
||||
PICO_FREE(mock->in_head);
|
||||
mock->in_head = nxt;
|
||||
}
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
int pico_mock_network_read(struct mock_device*mock, void *buf, int len)
|
||||
{
|
||||
struct mock_frame*nxt;
|
||||
if(mock->out_head == NULL)
|
||||
return 0;
|
||||
|
||||
if(len > mock->out_head->len - mock->out_head->read)
|
||||
len = mock->out_head->len - mock->out_head->read;
|
||||
|
||||
memcpy(buf, mock->out_head->buffer, (uint32_t)len);
|
||||
|
||||
if(len + mock->out_head->read != mock->out_head->len) {
|
||||
mock->out_head->read += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
PICO_FREE(mock->out_head->buffer);
|
||||
|
||||
if(mock->out_tail == mock->out_head) {
|
||||
PICO_FREE(mock->out_head);
|
||||
mock->out_tail = mock->out_head = NULL;
|
||||
return len;
|
||||
}
|
||||
|
||||
nxt = mock->out_head->next;
|
||||
PICO_FREE(mock->out_head);
|
||||
mock->out_head = nxt;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int pico_mock_network_write(struct mock_device*mock, const void *buf, int len)
|
||||
{
|
||||
struct mock_frame*frame;
|
||||
if (len > MOCK_MTU)
|
||||
return 0;
|
||||
|
||||
frame = PICO_ZALLOC(sizeof(struct mock_frame));
|
||||
if(!frame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(mock->in_head == NULL)
|
||||
mock->in_head = frame;
|
||||
else
|
||||
mock->in_tail->next = frame;
|
||||
|
||||
mock->in_tail = frame;
|
||||
|
||||
mock->in_tail->buffer = PICO_ZALLOC((uint32_t)len);
|
||||
if(!mock->in_tail->buffer)
|
||||
return 0;
|
||||
|
||||
memcpy(mock->in_tail->buffer, buf, (uint32_t)len);
|
||||
mock->in_tail->len = len;
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Public interface: create/destroy. */
|
||||
|
||||
void pico_mock_destroy(struct pico_device *dev)
|
||||
{
|
||||
struct mock_device search = {
|
||||
.dev = dev
|
||||
};
|
||||
struct mock_device*mock = pico_tree_findKey(&mock_device_tree, &search);
|
||||
struct mock_frame*nxt;
|
||||
|
||||
if(!mock)
|
||||
return;
|
||||
|
||||
nxt = mock->in_head;
|
||||
while(nxt != NULL) {
|
||||
mock->in_head = mock->in_head->next;
|
||||
PICO_FREE(nxt);
|
||||
nxt = mock->in_head;
|
||||
}
|
||||
nxt = mock->out_head;
|
||||
while(nxt != NULL) {
|
||||
mock->out_head = mock->out_head->next;
|
||||
PICO_FREE(nxt);
|
||||
nxt = mock->out_head;
|
||||
}
|
||||
pico_tree_delete(&mock_device_tree, mock);
|
||||
}
|
||||
|
||||
struct mock_device *pico_mock_create(uint8_t*mac)
|
||||
{
|
||||
|
||||
struct mock_device*mock = PICO_ZALLOC(sizeof(struct mock_device));
|
||||
if(!mock)
|
||||
return NULL;
|
||||
|
||||
mock->dev = PICO_ZALLOC(sizeof(struct pico_device));
|
||||
if (!mock->dev) {
|
||||
PICO_FREE(mock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(mac != NULL) {
|
||||
mock->mac = PICO_ZALLOC(6 * sizeof(uint8_t));
|
||||
if(!mock->mac) {
|
||||
PICO_FREE(mock->dev);
|
||||
PICO_FREE(mock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(mock->mac, mac, 6);
|
||||
}
|
||||
|
||||
if( 0 != pico_device_init((struct pico_device *)mock->dev, "mock", mac)) {
|
||||
dbg ("Loop init failed.\n");
|
||||
pico_device_destroy(mock->dev);
|
||||
if(mock->mac != NULL)
|
||||
PICO_FREE(mock->mac);
|
||||
|
||||
PICO_FREE(mock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mock->dev->send = pico_mock_send;
|
||||
mock->dev->poll = pico_mock_poll;
|
||||
mock->dev->destroy = pico_mock_destroy;
|
||||
dbg("Device %s created.\n", mock->dev->name);
|
||||
|
||||
if (pico_tree_insert(&mock_device_tree, mock)) {
|
||||
if (mock->mac != NULL)
|
||||
PICO_FREE(mock->mac);
|
||||
|
||||
pico_device_destroy(mock->dev);
|
||||
|
||||
PICO_FREE(mock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
/*
|
||||
* a few utility functions that check certain fields
|
||||
*/
|
||||
|
||||
uint32_t mock_get_sender_ip4(struct mock_device*mock, void*buf, int len)
|
||||
{
|
||||
uint32_t ret;
|
||||
int start = mock->mac ? 14 : 0;
|
||||
if(start + 16 > len) {
|
||||
dbg("out of range!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&ret, buf + start + 12, 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* find a way to create ARP replies
|
||||
*
|
||||
* create the other utility functions, e.g.
|
||||
* -is_arp_request
|
||||
* -create_arp_reply
|
||||
* -get_destination_ip4
|
||||
* -get_ip4_total_length
|
||||
* -is_ip4_checksum_valid
|
||||
* -is_tcp_syn
|
||||
* -create_tcp_synack
|
||||
* -is_tcp_checksum_valid
|
||||
* etc.
|
||||
*
|
||||
*/
|
||||
|
||||
int mock_ip_protocol(struct mock_device*mock, void*buf, int len)
|
||||
{
|
||||
uint8_t type;
|
||||
int start = mock->mac ? 14 : 0;
|
||||
if(start + 10 > len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&type, buf + start + 9, 1);
|
||||
return type;
|
||||
}
|
||||
|
||||
/* note : this function doesn't check if the IP header has any options */
|
||||
int mock_icmp_type(struct mock_device*mock, void*buf, int len)
|
||||
{
|
||||
uint8_t type;
|
||||
int start = mock->mac ? 14 : 0;
|
||||
if(start + 21 > len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&type, buf + start + 20, 1);
|
||||
return type;
|
||||
}
|
||||
|
||||
/* note : this function doesn't check if the IP header has any options */
|
||||
int mock_icmp_code(struct mock_device*mock, void*buf, int len)
|
||||
{
|
||||
uint8_t type;
|
||||
int start = mock->mac ? 14 : 0;
|
||||
if(start + 22 > len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&type, buf + start + 21, 1);
|
||||
return type;
|
||||
}
|
||||
47
kernel/picotcp/modules/pico_dev_mock.h
Normal file
47
kernel/picotcp/modules/pico_dev_mock.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_MOCK
|
||||
#define INCLUDE_PICO_MOCK
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
|
||||
struct mock_frame {
|
||||
uint8_t*buffer;
|
||||
int len;
|
||||
int read;
|
||||
|
||||
struct mock_frame*next;
|
||||
};
|
||||
|
||||
struct mock_device {
|
||||
struct pico_device*dev;
|
||||
struct mock_frame*in_head;
|
||||
struct mock_frame*in_tail;
|
||||
struct mock_frame*out_head;
|
||||
struct mock_frame*out_tail;
|
||||
|
||||
uint8_t*mac;
|
||||
|
||||
};
|
||||
|
||||
struct mock_device;
|
||||
/* A mockup-device for the purpose of testing. It provides a couple of extra "network"-functions, which represent the network-side of the device. A network_send will result in mock_poll reading something, a network_read will see if the stack has sent anything through our mock-device. */
|
||||
void pico_mock_destroy(struct pico_device *dev);
|
||||
struct mock_device *pico_mock_create(uint8_t*mac);
|
||||
|
||||
int pico_mock_network_read(struct mock_device*mock, void *buf, int len);
|
||||
int pico_mock_network_write(struct mock_device*mock, const void *buf, int len);
|
||||
|
||||
/* TODO */
|
||||
/* we could use a few checking functions, e.g. one to see if it's a valid IP packet, if it's TCP, if the IP-address matches,... */
|
||||
/* That would be useful to avoid having to manually create buffers of what you expect, probably with masks for things that are random,... */
|
||||
uint32_t mock_get_sender_ip4(struct mock_device*mock, void*buf, int len);
|
||||
|
||||
int mock_ip_protocol(struct mock_device*mock, void*buf, int len);
|
||||
int mock_icmp_type(struct mock_device*mock, void*buf, int len);
|
||||
int mock_icmp_code(struct mock_device*mock, void*buf, int len);
|
||||
#endif
|
||||
60
kernel/picotcp/modules/pico_dev_null.c
Normal file
60
kernel/picotcp/modules/pico_dev_null.c
Normal file
@ -0,0 +1,60 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_null.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
struct pico_device_null {
|
||||
struct pico_device dev;
|
||||
int statistics_frames_out;
|
||||
};
|
||||
|
||||
#define NULL_MTU 0
|
||||
|
||||
static int pico_null_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
struct pico_device_null *null = (struct pico_device_null *) dev;
|
||||
IGNORE_PARAMETER(buf);
|
||||
|
||||
/* Increase the statistic count */
|
||||
null->statistics_frames_out++;
|
||||
|
||||
/* Discard the frame content silently. */
|
||||
return len;
|
||||
}
|
||||
|
||||
static int pico_null_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
/* We never have packet to receive, no score is used. */
|
||||
IGNORE_PARAMETER(dev);
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
/* Public interface: create/destroy. */
|
||||
|
||||
|
||||
struct pico_device *pico_null_create(const char *name)
|
||||
{
|
||||
struct pico_device_null *null = PICO_ZALLOC(sizeof(struct pico_device_null));
|
||||
|
||||
if (!null)
|
||||
return NULL;
|
||||
|
||||
if( 0 != pico_device_init((struct pico_device *)null, name, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
null->dev.overhead = 0;
|
||||
null->statistics_frames_out = 0;
|
||||
null->dev.send = pico_null_send;
|
||||
null->dev.poll = pico_null_poll;
|
||||
dbg("Device %s created.\n", null->dev.name);
|
||||
return (struct pico_device *)null;
|
||||
}
|
||||
|
||||
15
kernel/picotcp/modules/pico_dev_null.h
Normal file
15
kernel/picotcp/modules/pico_dev_null.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_NULL
|
||||
#define INCLUDE_PICO_NULL
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
void pico_null_destroy(struct pico_device *null);
|
||||
struct pico_device *pico_null_create(const char *name);
|
||||
|
||||
#endif
|
||||
|
||||
96
kernel/picotcp/modules/pico_dev_pcap.c
Normal file
96
kernel/picotcp/modules/pico_dev_pcap.c
Normal file
@ -0,0 +1,96 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include <pcap.h>
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_pcap.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
#include <sys/poll.h>
|
||||
|
||||
struct pico_device_pcap {
|
||||
struct pico_device dev;
|
||||
pcap_t *conn;
|
||||
};
|
||||
|
||||
#define VDE_MTU 2048
|
||||
|
||||
static int pico_pcap_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
struct pico_device_pcap *pcap = (struct pico_device_pcap *) dev;
|
||||
/* dbg("[%s] send %d bytes.\n", dev->name, len); */
|
||||
return pcap_inject(pcap->conn, buf, (uint32_t)len);
|
||||
}
|
||||
|
||||
static void pico_dev_pcap_cb(u_char *u, const struct pcap_pkthdr *h, const u_char *data)
|
||||
{
|
||||
struct pico_device *dev = (struct pico_device *)u;
|
||||
const uint8_t *buf = (const uint8_t *)data;
|
||||
pico_stack_recv(dev, buf, (uint32_t)h->len);
|
||||
}
|
||||
|
||||
|
||||
static int pico_pcap_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct pico_device_pcap *pcap = (struct pico_device_pcap *) dev;
|
||||
loop_score -= pcap_dispatch(pcap->conn, loop_score, pico_dev_pcap_cb, (u_char *) pcap);
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
/* Public interface: create/destroy. */
|
||||
|
||||
void pico_pcap_destroy(struct pico_device *dev)
|
||||
{
|
||||
struct pico_device_pcap *pcap = (struct pico_device_pcap *) dev;
|
||||
pcap_close(pcap->conn);
|
||||
}
|
||||
|
||||
#define PICO_PCAP_MODE_LIVE 0
|
||||
#define PICO_PCAP_MODE_STORED 1
|
||||
|
||||
static struct pico_device *pico_pcap_create(char *if_file_name, char *name, uint8_t *mac, int mode)
|
||||
{
|
||||
struct pico_device_pcap *pcap = PICO_ZALLOC(sizeof(struct pico_device_pcap));
|
||||
char errbuf[2000];
|
||||
if (!pcap)
|
||||
return NULL;
|
||||
|
||||
if( 0 != pico_device_init((struct pico_device *)pcap, name, mac)) {
|
||||
dbg ("Pcap init failed.\n");
|
||||
pico_pcap_destroy((struct pico_device *)pcap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pcap->dev.overhead = 0;
|
||||
|
||||
if (mode == PICO_PCAP_MODE_LIVE)
|
||||
pcap->conn = pcap_open_live(if_file_name, 2000, 100, 10, errbuf);
|
||||
else
|
||||
pcap->conn = pcap_open_offline(if_file_name, errbuf);
|
||||
|
||||
if (!pcap->conn) {
|
||||
pico_pcap_destroy((struct pico_device *)pcap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pcap->dev.send = pico_pcap_send;
|
||||
pcap->dev.poll = pico_pcap_poll;
|
||||
pcap->dev.destroy = pico_pcap_destroy;
|
||||
dbg("Device %s created.\n", pcap->dev.name);
|
||||
return (struct pico_device *)pcap;
|
||||
}
|
||||
|
||||
struct pico_device *pico_pcap_create_fromfile(char *filename, char *name, uint8_t *mac)
|
||||
{
|
||||
return pico_pcap_create(filename, name, mac, PICO_PCAP_MODE_STORED);
|
||||
}
|
||||
|
||||
struct pico_device *pico_pcap_create_live(char *ifname, char *name, uint8_t *mac)
|
||||
{
|
||||
return pico_pcap_create(ifname, name, mac, PICO_PCAP_MODE_LIVE);
|
||||
}
|
||||
19
kernel/picotcp/modules/pico_dev_pcap.h
Normal file
19
kernel/picotcp/modules/pico_dev_pcap.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
|
||||
Author: Daniele Lacamera <daniele.lacamera@altran.com>
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_PCAP
|
||||
#define INCLUDE_PICO_PCAP
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
#include <pcap.h>
|
||||
|
||||
void pico_pcap_destroy(struct pico_device *pcap);
|
||||
struct pico_device *pico_pcap_create_live(char *ifname, char *name, uint8_t *mac);
|
||||
struct pico_device *pico_pcap_create_fromfile(char *filename, char *name, uint8_t *mac);
|
||||
|
||||
#endif
|
||||
|
||||
2312
kernel/picotcp/modules/pico_dev_ppp.c
Normal file
2312
kernel/picotcp/modules/pico_dev_ppp.c
Normal file
File diff suppressed because it is too large
Load Diff
26
kernel/picotcp/modules/pico_dev_ppp.h
Normal file
26
kernel/picotcp/modules/pico_dev_ppp.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_PPP
|
||||
#define INCLUDE_PICO_PPP
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
void pico_ppp_destroy(struct pico_device *ppp);
|
||||
struct pico_device *pico_ppp_create(void);
|
||||
|
||||
int pico_ppp_connect(struct pico_device *dev);
|
||||
int pico_ppp_disconnect(struct pico_device *dev);
|
||||
|
||||
int pico_ppp_set_serial_read(struct pico_device *dev, int (*sread)(struct pico_device *, void *, int));
|
||||
int pico_ppp_set_serial_write(struct pico_device *dev, int (*swrite)(struct pico_device *, const void *, int));
|
||||
int pico_ppp_set_serial_set_speed(struct pico_device *dev, int (*sspeed)(struct pico_device *, uint32_t));
|
||||
|
||||
int pico_ppp_set_apn(struct pico_device *dev, const char *apn);
|
||||
int pico_ppp_set_username(struct pico_device *dev, const char *username);
|
||||
int pico_ppp_set_password(struct pico_device *dev, const char *password);
|
||||
|
||||
#endif /* INCLUDE_PICO_PPP */
|
||||
357
kernel/picotcp/modules/pico_dev_radio_mgr.c
Normal file
357
kernel/picotcp/modules/pico_dev_radio_mgr.c
Normal file
@ -0,0 +1,357 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
/*
|
||||
* For testing purposes. pico_dev_radio_manager allows simulating a mesh
|
||||
* network for smoke tests. I previously used geomess, but that's another
|
||||
* dependency to add then. Then @danielinux wrote the pico_dev_radiotest but
|
||||
* that required adding a multicast route on the host which in its turn
|
||||
* required 'sudo'. So I wrote a small simulator which doesn't require sudo.
|
||||
* - Jelle
|
||||
*/
|
||||
|
||||
#include "pico_dev_radiotest.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_dev_tap.h"
|
||||
#include "pico_802154.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_dev_radio_mgr.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/poll.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef DEBUG_RADIOTEST
|
||||
#define RADIO_DBG dbg
|
||||
#else
|
||||
#define RADIO_DBG(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define LISTENING_PORT 7777
|
||||
#define MESSAGE_MTU 150
|
||||
#define EVER (;;)
|
||||
|
||||
struct socket {
|
||||
int s;
|
||||
uint8_t mgr;
|
||||
uint8_t id;
|
||||
uint8_t area0;
|
||||
uint8_t area1;
|
||||
};
|
||||
|
||||
/* Compare two application sockets */
|
||||
static int
|
||||
pico_radio_mgr_sock_cmp(void *a, void *b)
|
||||
{
|
||||
struct socket *sa = a, *sb = b;
|
||||
return (int)(sa->id - sb->id);
|
||||
}
|
||||
|
||||
PICO_TREE_DECLARE(Sockets, pico_radio_mgr_sock_cmp);
|
||||
|
||||
/* Insert a new socket in the tree */
|
||||
static int
|
||||
pico_radio_mgr_socket_insert(int socket, uint8_t id, uint8_t area0, uint8_t area1, uint8_t mgr)
|
||||
{
|
||||
struct socket *s = PICO_ZALLOC(sizeof(struct socket));
|
||||
if (s) {
|
||||
s->area0 = area0;
|
||||
s->area1 = area1;
|
||||
s->s = socket;
|
||||
s->mgr = mgr;
|
||||
s->id = id;
|
||||
if (!pico_tree_insert(&Sockets, s))
|
||||
return 0;
|
||||
PICO_FREE(s);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Gather an array of poll descriptors with all sockets */
|
||||
static struct pollfd *
|
||||
pico_radio_mgr_socket_all(int *n)
|
||||
{
|
||||
struct pico_tree_node *i = NULL;
|
||||
struct socket *key = NULL;
|
||||
struct pollfd *fds = NULL;
|
||||
int j = 1;
|
||||
*n = 0;
|
||||
|
||||
/* Retrieve all sockets */
|
||||
pico_tree_foreach(i, &Sockets) {
|
||||
(*n)++;
|
||||
}
|
||||
|
||||
/* Create array from tree */
|
||||
fds = PICO_ZALLOC(sizeof(struct pollfd) * (size_t)*n);
|
||||
if (fds) {
|
||||
/* Put every socket in array */
|
||||
pico_tree_foreach(i, &Sockets) {
|
||||
if (i && (key = i->keyValue)) {
|
||||
if (!key->id) {
|
||||
fds[0].fd = key->s;
|
||||
fds[0].events = POLLIN;
|
||||
} else {
|
||||
fds[j].fd = key->s;
|
||||
fds[j].events = POLLIN | POLLHUP;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fds;
|
||||
}
|
||||
|
||||
/* Get connection socket that belongs to a particular node */
|
||||
static struct socket *
|
||||
pico_radio_mgr_socket_node(uint8_t id)
|
||||
{
|
||||
struct socket test = { 0, 0, id };
|
||||
return pico_tree_findKey(&Sockets, &test);
|
||||
}
|
||||
|
||||
/* Handle POLLHUP event */
|
||||
static int
|
||||
pico_radio_mgr_socket_hup(int socket)
|
||||
{
|
||||
struct pico_tree_node *i = NULL;
|
||||
struct socket *key = NULL;
|
||||
|
||||
pico_tree_foreach(i, &Sockets) {
|
||||
key = i->keyValue;
|
||||
if (key && key->s == socket) {
|
||||
pico_tree_delete(&Sockets, key);
|
||||
RADIO_DBG("Radio %d detached from network\n", key->id);
|
||||
PICO_FREE(key);
|
||||
close(socket);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Receive's an 'Hello'-message from the node that contains the id, the inserts
|
||||
* an entry in the Sockets-tree */
|
||||
static int
|
||||
pico_radio_mgr_welcome(int socket)
|
||||
{
|
||||
int ret_len = sizeof(uint8_t);
|
||||
uint8_t id = 0, area0, area1;
|
||||
|
||||
errno = 0;
|
||||
while ((ret_len = recv(socket, &id, (size_t)ret_len, 0)) != 1) {
|
||||
if (errno && EINTR != errno)
|
||||
goto hup;
|
||||
}
|
||||
while ((ret_len = recv(socket, &area0, (size_t)ret_len, 0)) != 1) {
|
||||
if (errno && EINTR != errno)
|
||||
goto hup;
|
||||
}
|
||||
while ((ret_len = recv(socket, &area1, (size_t)ret_len, 0)) != 1) {
|
||||
if (errno && EINTR != errno)
|
||||
goto hup;
|
||||
}
|
||||
|
||||
if (id <= 0) { // Node's can't have ID '0'.
|
||||
RADIO_DBG("Invalid socket\n");
|
||||
close(socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
RADIO_DBG("Connected to node %u in area %u and %u on socket %d.\n", id, area0, area1, socket);
|
||||
if (pico_radio_mgr_socket_insert(socket, id, area0, area1, 0)) {
|
||||
RADIO_DBG("Failed inserting new socket\n");
|
||||
close(socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
hup:
|
||||
RADIO_DBG("recv() failed with error: %s\n", strerror(errno));
|
||||
close(socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Accepts a new TCP connection request */
|
||||
static int
|
||||
pico_radio_mgr_accept(int socket)
|
||||
{
|
||||
unsigned int len = sizeof(struct sockaddr_in);
|
||||
struct sockaddr_in addr;
|
||||
int s = accept(socket, (struct sockaddr *)&addr, &len);
|
||||
if (s < 0) {
|
||||
RADIO_DBG("Failed accepting connection\n");
|
||||
return s;
|
||||
} else if (!s) {
|
||||
RADIO_DBG("accept() returned file descriptor '%d'\n", s);
|
||||
return s;
|
||||
}
|
||||
return pico_radio_mgr_welcome(s);
|
||||
}
|
||||
|
||||
/* Start listening for TCP connection requests on 'LISTENING_PORT' */
|
||||
static int
|
||||
pico_radio_mgr_listen(void)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
int ret = 0, yes = 1;
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LISTENING_PORT);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
||||
|
||||
ret = bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
|
||||
if (ret < 0) {
|
||||
RADIO_DBG("Failed binding socket to address: %s\n", strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = listen(s, 5);
|
||||
if (ret < 0) {
|
||||
RADIO_DBG("Failed start listening\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Also insert server socket in tree for polling */
|
||||
if (pico_radio_mgr_socket_insert(s, 0, 0, 0, 1)) {
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbg("Started listening on port %d\n", LISTENING_PORT);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Distribute received frame over all the areas where the node is attached to */
|
||||
static void
|
||||
pico_radio_mgr_distribute(uint8_t *buf, int len, uint8_t id)
|
||||
{
|
||||
struct socket *node = pico_radio_mgr_socket_node(id);
|
||||
uint8_t area0 = 0, area1 = 0, ar0 = 0, ar1 = 0, phy = (uint8_t)len;
|
||||
struct pico_tree_node *i = NULL;
|
||||
struct socket *key = NULL;
|
||||
if (node) {
|
||||
RADIO_DBG("Received frame from node '%d' of '%d' bytes\n", id, len);
|
||||
area0 = node->area0;
|
||||
area1 = node->area1;
|
||||
} else {
|
||||
RADIO_DBG("Received frame from node not connected to network, weird..\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pico_tree_foreach(i, &Sockets) {
|
||||
key = i->keyValue;
|
||||
if (key && key->id != id && key->id) { // Do not sent to ourselves or manager
|
||||
ar0 = key->area0;
|
||||
ar1 = key->area1;
|
||||
if (area0 == ar0 || area0 == ar1 || (area1 && (area1 == ar0 || area1 == ar1))) {
|
||||
len = (int)sendto(key->s, &phy, (size_t)1, 0, NULL, 0);
|
||||
if (len != 1) return;
|
||||
len = (int)sendto(key->s, buf, (size_t)phy, 0, NULL, 0);
|
||||
if (len == (int)phy)
|
||||
RADIO_DBG("Forwarded from '%u' of %d bytes sent to '%u'\n", id, len, key->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Process poll-events */
|
||||
static void
|
||||
pico_radio_mgr_process(struct pollfd *fds, int n)
|
||||
{
|
||||
uint8_t buf[MESSAGE_MTU] = { 0 }, node = 0, phy = 0;
|
||||
int i = 0, ret_len = 0;
|
||||
short event = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
event = fds[i].revents;
|
||||
if (event && (event & POLLIN)) { // POLLIN
|
||||
if (!i) {
|
||||
/* Accept a new connection */
|
||||
pico_radio_mgr_accept(fds[i].fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read from node */
|
||||
ret_len = (int)recv(fds[i].fd, &phy, (size_t)1, 0);
|
||||
if (ret_len <= 0)
|
||||
goto hup;
|
||||
ret_len = (int)recv(fds[i].fd, buf, (size_t)phy, 0);
|
||||
if (ret_len <= 0 || ret_len != phy)
|
||||
goto hup;
|
||||
node = buf[ret_len - 2];
|
||||
pico_radio_mgr_distribute(buf, ret_len, node);
|
||||
} else if (event && (event & POLLHUP)) {
|
||||
goto hup;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
hup:
|
||||
pico_radio_mgr_socket_hup(fds[i].fd);
|
||||
}
|
||||
|
||||
static void
|
||||
pico_radio_mgr_quit(int signum)
|
||||
{
|
||||
struct pico_tree_node *i = NULL, *tmp = NULL;
|
||||
struct socket *key = NULL;
|
||||
IGNORE_PARAMETER(signum);
|
||||
|
||||
dbg("Closing all sockets...");
|
||||
pico_tree_foreach_safe(i, &Sockets, tmp) {
|
||||
key = i->keyValue;
|
||||
if (key) {
|
||||
pico_tree_delete(&Sockets, key);
|
||||
shutdown(key->s, SHUT_RDWR);
|
||||
PICO_FREE(key);
|
||||
}
|
||||
}
|
||||
dbg("done.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Create and start a radio-manager instance */
|
||||
int
|
||||
pico_radio_mgr_start(void)
|
||||
{
|
||||
int server = pico_radio_mgr_listen();
|
||||
struct pollfd *fds = NULL;
|
||||
nfds_t n = 0;
|
||||
int ret = 0;
|
||||
if (server < 0)
|
||||
return -1;
|
||||
|
||||
signal(SIGQUIT, pico_radio_mgr_quit);
|
||||
|
||||
for EVER {
|
||||
if (fds)
|
||||
PICO_FREE(fds);
|
||||
fds = pico_radio_mgr_socket_all((int *)&n);
|
||||
errno = 0;
|
||||
ret = poll(fds, n, 1);
|
||||
if (errno != EINTR && ret < 0) {
|
||||
RADIO_DBG("Socket error: %s\n", strerror(ret));
|
||||
return ret;
|
||||
} else if (!ret) {
|
||||
continue;
|
||||
}
|
||||
pico_radio_mgr_process(fds, (int)n);
|
||||
}
|
||||
}
|
||||
14
kernel/picotcp/modules/pico_dev_radio_mgr.h
Normal file
14
kernel/picotcp/modules/pico_dev_radio_mgr.h
Normal file
@ -0,0 +1,14 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef __PICO_DEV_RADIO_MGR_H_
|
||||
#define __PICO_DEV_RADIO_MGR_H_
|
||||
|
||||
/* Start listening for TCP connection requests on 'LISTENING_PORT' */
|
||||
int pico_radio_mgr_start(void);
|
||||
|
||||
#endif
|
||||
486
kernel/picotcp/modules/pico_dev_radiotest.c
Normal file
486
kernel/picotcp/modules/pico_dev_radiotest.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Daniele Lacamera, Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* PicoTCP
|
||||
******************************************************************************/
|
||||
|
||||
#include "pico_dev_radiotest.h"
|
||||
#include "pico_6lowpan_ll.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_dev_tap.h"
|
||||
#include "pico_802154.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* System sockets
|
||||
******************************************************************************/
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/poll.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define LISTENING_PORT 7777
|
||||
#define MESSAGE_MTU 150
|
||||
|
||||
#ifdef RADIO_PCAP
|
||||
#include <pcap.h>
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_RADIOTEST
|
||||
#define RADIO_DBG dbg
|
||||
#else
|
||||
#define RADIO_DBG(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Constants
|
||||
******************************************************************************/
|
||||
|
||||
/* Uncomment next line to enable Random packet loss (specify percentage) */
|
||||
//#define PACKET_LOSS 3
|
||||
|
||||
#define RFDEV_PANID 0xABCD
|
||||
#define MC_ADDR_BE 0x010101EBU
|
||||
#define LO_ADDR 0x0100007FU
|
||||
|
||||
#define LOOP_MTU 127
|
||||
|
||||
/*******************************************************************************
|
||||
* Type definitions
|
||||
******************************************************************************/
|
||||
|
||||
struct radiotest_radio {
|
||||
struct pico_dev_6lowpan dev;
|
||||
struct pico_6lowpan_info addr;
|
||||
int sock0;
|
||||
int sock1;
|
||||
#ifdef RADIO_PCAP
|
||||
pcap_t *pcap;
|
||||
pcap_dumper_t *pcapd;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct radiotest_frame
|
||||
{
|
||||
uint8_t *buf;
|
||||
int len;
|
||||
uint32_t id;
|
||||
union pico_ll_addr src;
|
||||
union pico_ll_addr dst;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Global variables
|
||||
******************************************************************************/
|
||||
|
||||
static int connection = 0;
|
||||
|
||||
static uint32_t tx_id = 0;
|
||||
static uint32_t rx_id = 0;
|
||||
|
||||
/*******************************************************************************
|
||||
* pcap
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef RADIO_PCAP
|
||||
|
||||
static void radiotest_pcap_open(struct radiotest_radio *dev, char *dump)
|
||||
{
|
||||
char path[100];
|
||||
|
||||
/* Open offline packet capture */
|
||||
#ifdef PICO_SUPPORT_802154
|
||||
dev->pcap = pcap_open_dead(DLT_IEEE802_15_4, 65535);
|
||||
#elif defined (PICO_SUPPORT_802154_NO_MAC)
|
||||
dev->pcap = pcap_open_dead(DLT_RAW, 65535);
|
||||
#endif
|
||||
if (!dev->pcap) {
|
||||
perror("LibPCAP");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Construct file path */
|
||||
snprintf(path, 100, dump, dev->addr);
|
||||
|
||||
/* Open dump */
|
||||
dev->pcapd = pcap_dump_open(dev->pcap, path);
|
||||
if (dev->pcapd)
|
||||
dbg("PCAP Enabled\n");
|
||||
else
|
||||
dbg("PCAP Disabled\n");
|
||||
}
|
||||
|
||||
static void radiotest_pcap_write(struct radiotest_radio *dev, uint8_t *buf, int len)
|
||||
{
|
||||
struct pcap_pkthdr ph;
|
||||
if (!dev || !dev->pcapd)
|
||||
return;
|
||||
ph.caplen = (uint32_t)len;
|
||||
ph.len = (uint32_t)len;
|
||||
gettimeofday(&ph.ts, NULL);
|
||||
pcap_dump((u_char *)dev->pcapd, &ph, buf);
|
||||
pcap_dump_flush(dev->pcapd);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void radiotest_pcap_open(struct radiotest_radio *dev, char *dump)
|
||||
{
|
||||
(void)dev;
|
||||
(void)dump;
|
||||
}
|
||||
|
||||
static void radiotest_pcap_write(struct radiotest_radio *dev, uint8_t *buf, int len)
|
||||
{
|
||||
(void)dev;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int radiotest_cmp(void *a, void *b)
|
||||
{
|
||||
struct radiotest_frame *fa = (struct radiotest_frame *)a;
|
||||
struct radiotest_frame *fb = (struct radiotest_frame *)b;
|
||||
return (int)(fa->id - fb->id);
|
||||
}
|
||||
|
||||
PICO_TREE_DECLARE(LoopFrames, radiotest_cmp);
|
||||
|
||||
static uint8_t *radiotest_nxt_rx(int *len, union pico_ll_addr *src, union pico_ll_addr *dst)
|
||||
{
|
||||
struct radiotest_frame test, *found = NULL;
|
||||
uint8_t *ret = NULL;
|
||||
test.id = rx_id++;
|
||||
|
||||
found = pico_tree_findKey(&LoopFrames, &test);
|
||||
if (found) {
|
||||
ret = found->buf;
|
||||
*len = found->len;
|
||||
*src = found->src;
|
||||
*dst = found->dst;
|
||||
pico_tree_delete(&LoopFrames, found);
|
||||
PICO_FREE(found);
|
||||
} else {
|
||||
rx_id--;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void radiotest_nxt_tx(uint8_t *buf, int len, union pico_ll_addr src, union pico_ll_addr dst)
|
||||
{
|
||||
struct radiotest_frame *new = PICO_ZALLOC(sizeof(struct radiotest_frame));
|
||||
if (new) {
|
||||
new->buf = PICO_ZALLOC((uint16_t)len);
|
||||
if (new->buf) {
|
||||
memcpy(new->buf, buf, (size_t)len);
|
||||
new->len = len;
|
||||
new->id = tx_id++;
|
||||
new->src = src;
|
||||
new->dst = dst;
|
||||
if (pico_tree_insert(&LoopFrames, new)) {
|
||||
PICO_FREE(new);
|
||||
tx_id--;
|
||||
}
|
||||
} else {
|
||||
PICO_FREE(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pico_loop_send(struct pico_device *dev, void *buf, int len, union pico_ll_addr src, union pico_ll_addr dst)
|
||||
{
|
||||
IGNORE_PARAMETER(dev);
|
||||
if (len > LOOP_MTU)
|
||||
return 0;
|
||||
RADIO_DBG("Looping back frame of %d bytes.\n", len);
|
||||
radiotest_nxt_tx(buf, len, src, dst);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int pico_loop_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
union pico_ll_addr src, dst;
|
||||
uint8_t *buf = NULL;
|
||||
int len = 0;
|
||||
|
||||
if (loop_score <= 0)
|
||||
return 0;
|
||||
|
||||
buf = radiotest_nxt_rx(&len, &src, &dst);
|
||||
if (buf) {
|
||||
RADIO_DBG("Receiving frame of %d bytes.\n", len);
|
||||
pico_6lowpan_stack_recv(dev, buf, (uint32_t)len, &src, &dst);
|
||||
PICO_FREE(buf);
|
||||
loop_score--;
|
||||
}
|
||||
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
|
||||
/* Generates a simple extended address */
|
||||
static void radiotest_gen_ex(struct pico_6lowpan_short addr_short, uint8_t *buf)
|
||||
{
|
||||
uint16_t sh = addr_short.addr;
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x00;
|
||||
buf[3] = 0xaa;
|
||||
buf[4] = 0xab;
|
||||
buf[5] = 0x00;
|
||||
buf[6] = (uint8_t)((uint8_t)(short_be(sh) & 0xFF00) >> 8u);
|
||||
buf[7] = (uint8_t)(short_be(sh) & 0xFFu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulated CRC16-CITT Kermit generation
|
||||
*
|
||||
* @param buf uint8_t *, buffer to generate FCS for.
|
||||
* @param len uint8_t, len of the buffer
|
||||
*
|
||||
* @return CITT Kermit CRC16 of the buffer
|
||||
*/
|
||||
static uint16_t calculate_crc16(uint8_t *buf, uint8_t len)
|
||||
{
|
||||
uint16_t crc = 0x0000;
|
||||
uint16_t q = 0, i = 0;
|
||||
uint8_t c = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
c = buf[i];
|
||||
q = (crc ^ c) & 0x0F;
|
||||
crc = (uint16_t)((uint16_t)(crc >> 4) ^ (q * 0x1081));
|
||||
q = (crc ^ (c >> 4)) & 0xF;
|
||||
crc = (uint16_t)((uint16_t)(crc >> 4) ^ (q * 0x1081));
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Poll-function for the pico_device-structure */
|
||||
static int radiotest_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct radiotest_radio *radio = (struct radiotest_radio *)dev;
|
||||
union pico_ll_addr src = {0}, dst = {0};
|
||||
int pollret, ret_len;
|
||||
struct pollfd p;
|
||||
uint8_t buf[128];
|
||||
uint8_t phy = 0;
|
||||
|
||||
if (loop_score <= 0)
|
||||
return 0;
|
||||
|
||||
if (!dev)
|
||||
return loop_score;
|
||||
|
||||
p.fd = connection;
|
||||
p.events = POLLIN | POLLHUP;
|
||||
|
||||
/* Poll for data from radio management */
|
||||
errno = 0;
|
||||
pollret = poll(&p, (nfds_t)1, 1);
|
||||
if (errno == EINTR || pollret == 0)
|
||||
return loop_score;
|
||||
|
||||
if (pollret < 0) {
|
||||
fprintf(stderr, "Socket error %s!\n", strerror(errno));
|
||||
exit(5);
|
||||
}
|
||||
|
||||
if (p.revents & POLLIN) {
|
||||
ret_len = (int)recv(connection, &phy, (size_t)1, 0);
|
||||
if (ret_len != 1) return loop_score;
|
||||
ret_len = (int)recv(connection, buf, (size_t)phy, 0);
|
||||
if (ret_len != (int)phy)
|
||||
return loop_score;
|
||||
else if (!ret_len) {
|
||||
RADIO_DBG("Radio manager detached from network\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_len < 2) { /* Not valid */
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
#ifdef P_LOSS
|
||||
long n = lrand48();
|
||||
n = n % 100;
|
||||
if (n < P_LOSS) {
|
||||
RADIO_DBG("Packet got lost!\n");
|
||||
return loop_score;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ADDRESS FILTER */
|
||||
if (buf[ret_len - 1] != 0xFF && buf[ret_len - 1] != (uint8_t)short_be(radio->addr.addr_short.addr)) {
|
||||
RADIO_DBG("Packet is not for me!\n");
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
/* Get src and destination address */
|
||||
dst.pan.addr._ext = radio->addr.addr_ext;
|
||||
src.pan.addr.data[3] = 0xAA;
|
||||
src.pan.addr.data[4] = 0xAB;
|
||||
src.pan.addr.data[7] = buf[ret_len - 1];
|
||||
src.pan.mode = AM_6LOWPAN_EXT;
|
||||
ret_len -= 2;
|
||||
|
||||
/* Write the received frame to the pcap-dump */
|
||||
radiotest_pcap_write(radio, buf, ret_len);
|
||||
|
||||
/* Hand the frame over to pico */
|
||||
pico_6lowpan_stack_recv(dev, buf, (uint32_t)(ret_len - 2), &src, &dst);
|
||||
loop_score--;
|
||||
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
#define RADIO_OVERHEAD 4
|
||||
|
||||
/* Send-function for the pico_device-structure */
|
||||
static int radiotest_send(struct pico_device *dev, void *_buf, int len, union pico_ll_addr src, union pico_ll_addr dst)
|
||||
{
|
||||
struct radiotest_radio *radio = (struct radiotest_radio *)dev;
|
||||
uint8_t *buf = PICO_ZALLOC((size_t)(len + RADIO_OVERHEAD));
|
||||
uint8_t phy = 0, did = 0;
|
||||
uint16_t crc = 0;
|
||||
int ret = 0, dlen = 0;
|
||||
IGNORE_PARAMETER(src);
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
/* Try to get node-ID from address */
|
||||
if (dev && pico_6lowpan_lls[dev->mode].addr_len) {
|
||||
dlen = pico_6lowpan_lls[dev->mode].addr_len(&dst);
|
||||
if (dlen < 0)
|
||||
return -1;
|
||||
did = dst.pan.addr.data[dlen - 1];
|
||||
}
|
||||
|
||||
/* Store the addresses in buffer for management */
|
||||
memcpy(buf, _buf, (size_t)len);
|
||||
len = (uint16_t)(len + (uint16_t)RADIO_OVERHEAD); // CRC + ID
|
||||
buf[len - 2] = (uint8_t)short_be(radio->addr.addr_short.addr);
|
||||
buf[len - 1] = did;
|
||||
|
||||
/* Generate FCS, keep pcap happy ... */
|
||||
crc = calculate_crc16(_buf, (uint8_t)(len - RADIO_OVERHEAD));
|
||||
memcpy(buf + len - RADIO_OVERHEAD, (void *)&crc, 2);
|
||||
|
||||
/* Send frame to radio management */
|
||||
phy = (uint8_t)(len);
|
||||
ret = (int)sendto(connection, &phy, 1, 0, NULL, 0);
|
||||
if (ret != 1)
|
||||
return -1;
|
||||
ret = (int)sendto(connection, buf, (size_t)(len), 0, NULL, 0);
|
||||
RADIO_DBG("Radio '%u' transmitted a frame of %d bytes.\n", buf[len - 2], ret);
|
||||
|
||||
/* Write the sent frame to the pcap-dump */
|
||||
radiotest_pcap_write(radio, buf, len - 2);
|
||||
|
||||
PICO_FREE(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int radiotest_hello(int s, uint8_t id, uint8_t area0, uint8_t area1)
|
||||
{
|
||||
uint8_t buf[3] = { id, area0, area1 };
|
||||
if (sendto(s, buf, (size_t)3, 0, NULL, 0) != 3) {
|
||||
RADIO_DBG("Radio '%u' failed to send hello message\n", id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
RADIO_DBG("Radio '%u' attached to network\n", id);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int radiotest_connect(uint8_t id, uint8_t area0, uint8_t area1)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
int ret = 0;
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LISTENING_PORT);
|
||||
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
|
||||
|
||||
ret = connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
|
||||
if (ret) {
|
||||
RADIO_DBG("Radio '%u' could not attach to network\n", id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return radiotest_hello(s, id, area0, area1);
|
||||
}
|
||||
|
||||
static void
|
||||
pico_radiotest_quit(int signum)
|
||||
{
|
||||
IGNORE_PARAMETER(signum);
|
||||
dbg("Quitting radiotest\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Creates a radiotest-device */
|
||||
struct pico_device *pico_radiotest_create(uint8_t addr, uint8_t area0, uint8_t area1, int loop, char *dump)
|
||||
{
|
||||
struct radiotest_radio *radio = PICO_ZALLOC(sizeof(struct radiotest_radio));
|
||||
struct pico_dev_6lowpan *lp = (struct pico_dev_6lowpan *)radio;
|
||||
if (!radio)
|
||||
return NULL;
|
||||
if (!addr || (addr && !area0)) {
|
||||
RADIO_DBG("Usage (node): -6 [1-255],[1-255],[0-255] ...\n");
|
||||
}
|
||||
|
||||
signal(SIGQUIT, pico_radiotest_quit);
|
||||
|
||||
radio->addr.pan_id.addr = short_be(RFDEV_PANID);
|
||||
radio->addr.addr_short.addr = short_be((uint16_t)addr);
|
||||
radiotest_gen_ex(radio->addr.addr_short, radio->addr.addr_ext.addr);
|
||||
RADIO_DBG("Radiotest short address: 0x%04X\n", short_be(radio->addr.addr_short.addr));
|
||||
RADIO_DBG("Radiotest ext address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
radio->addr.addr_ext.addr[0],radio->addr.addr_ext.addr[1],
|
||||
radio->addr.addr_ext.addr[2],radio->addr.addr_ext.addr[3],
|
||||
radio->addr.addr_ext.addr[4],radio->addr.addr_ext.addr[5],
|
||||
radio->addr.addr_ext.addr[6],radio->addr.addr_ext.addr[7]);
|
||||
|
||||
if (!loop) {
|
||||
if ((connection = radiotest_connect(addr, area0, area1)) <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (pico_dev_6lowpan_init(lp, "radio", (uint8_t *)&radio->addr, LL_MODE_IEEE802154, MTU_802154_MAC, 0, radiotest_send, radiotest_poll)) {
|
||||
RADIO_DBG("pico_device_init failed.\n");
|
||||
pico_device_destroy((struct pico_device *)lp);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (pico_dev_6lowpan_init(lp, "radio", (uint8_t *)&radio->addr, LL_MODE_IEEE802154, MTU_802154_MAC, 0, pico_loop_send, pico_loop_poll)) {
|
||||
RADIO_DBG("pico_device_init failed.\n");
|
||||
pico_device_destroy((struct pico_device *)lp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dump) {
|
||||
dbg("Dump: %s\n", dump);
|
||||
radiotest_pcap_open(radio, dump);
|
||||
}
|
||||
|
||||
return (struct pico_device *)lp;
|
||||
}
|
||||
|
||||
16
kernel/picotcp/modules/pico_dev_radiotest.h
Normal file
16
kernel/picotcp/modules/pico_dev_radiotest.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Daniele Lacamera, Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_DEV_RADIOTEST
|
||||
#define INCLUDE_PICO_DEV_RADIOTEST
|
||||
|
||||
#include "pico_device.h"
|
||||
#include "pico_config.h"
|
||||
|
||||
struct pico_device *pico_radiotest_create(uint8_t addr, uint8_t area0, uint8_t area1, int loop, char *dump);
|
||||
|
||||
#endif /* INCLUDE_PICO_DEV_RADIOTEST */
|
||||
230
kernel/picotcp/modules/pico_dev_tap.c
Normal file
230
kernel/picotcp/modules/pico_dev_tap.c
Normal file
@ -0,0 +1,230 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <signal.h>
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_tap.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
#include <linux/if_tun.h>
|
||||
#endif
|
||||
|
||||
#include <sys/poll.h>
|
||||
|
||||
struct pico_device_tap {
|
||||
struct pico_device dev;
|
||||
int fd;
|
||||
};
|
||||
|
||||
#define TUN_MTU 2048
|
||||
|
||||
/* We only support one global link state - we only have two USR signals, we */
|
||||
/* can't spread these out over an arbitrary amount of devices. When you unplug */
|
||||
/* one tap, you unplug all of them. */
|
||||
|
||||
static int tapdev_link_state = 0;
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
if (signo == SIGUSR1) {
|
||||
tapdev_link_state = 0;
|
||||
}
|
||||
|
||||
if (signo == SIGUSR2) {
|
||||
tapdev_link_state = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int tap_link_state(__attribute__((unused)) struct pico_device *self)
|
||||
{
|
||||
return tapdev_link_state;
|
||||
}
|
||||
|
||||
|
||||
static int pico_tap_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
|
||||
return (int)write(tap->fd, buf, (uint32_t)len);
|
||||
}
|
||||
|
||||
static int pico_tap_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
|
||||
struct pollfd pfd;
|
||||
unsigned char buf[TUN_MTU];
|
||||
int len;
|
||||
pfd.fd = tap->fd;
|
||||
pfd.events = POLLIN;
|
||||
do {
|
||||
if (poll(&pfd, 1, 0) <= 0) {
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
len = (int)read(tap->fd, buf, TUN_MTU);
|
||||
if (len > 0) {
|
||||
loop_score--;
|
||||
pico_stack_recv(dev, buf, (uint32_t)len);
|
||||
}
|
||||
} while(loop_score > 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Public interface: create/destroy. */
|
||||
|
||||
void pico_tap_destroy(struct pico_device *dev)
|
||||
{
|
||||
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
|
||||
if(tap->fd > 0) {
|
||||
close(tap->fd);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
static int tap_open(char *name)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int tap_fd;
|
||||
if((tap_fd = open("/dev/net/tun", O_RDWR)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
strncpy(ifr.ifr_name, name, IFNAMSIZ);
|
||||
if(ioctl(tap_fd, TUNSETIFF, &ifr) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tap_fd;
|
||||
}
|
||||
#else
|
||||
static int tap_open(char *name)
|
||||
{
|
||||
int tap_fd;
|
||||
(void)name;
|
||||
tap_fd = open("/dev/tap0", O_RDWR);
|
||||
return tap_fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
static int tap_get_mac(char *name, uint8_t *mac)
|
||||
{
|
||||
int sck;
|
||||
struct ifreq eth;
|
||||
int retval = -1;
|
||||
|
||||
sck = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if(sck < 0) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
memset(ð, 0, sizeof(struct ifreq));
|
||||
strcpy(eth.ifr_name, name);
|
||||
/* call the IOCTL */
|
||||
if (ioctl(sck, SIOCGIFHWADDR, ð) < 0) {
|
||||
perror("ioctl(SIOCGIFHWADDR)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy (mac, ð.ifr_hwaddr.sa_data, 6);
|
||||
close(sck);
|
||||
return 0;
|
||||
|
||||
}
|
||||
#else
|
||||
#include <net/if_dl.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if_types.h>
|
||||
static int tap_get_mac(char *name, uint8_t *mac)
|
||||
{
|
||||
struct sockaddr_dl *sdl;
|
||||
struct ifaddrs *ifap, *root;
|
||||
if (getifaddrs(&ifap) != 0)
|
||||
return -1;
|
||||
|
||||
root = ifap;
|
||||
while(ifap) {
|
||||
if (strcmp(name, ifap->ifa_name) == 0) {
|
||||
sdl = (struct sockaddr_dl *) ifap->ifa_addr;
|
||||
}
|
||||
|
||||
if (sdl->sdl_type == IFT_ETHER) {
|
||||
memcpy(mac, LLADDR(sdl), 6);
|
||||
freeifaddrs(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ifap = ifap->ifa_next;
|
||||
}
|
||||
freeifaddrs(root);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct pico_device *pico_tap_create(char *name)
|
||||
{
|
||||
struct pico_device_tap *tap = PICO_ZALLOC(sizeof(struct pico_device_tap));
|
||||
uint8_t mac[6] = {};
|
||||
struct sigaction sa;
|
||||
|
||||
if (!tap) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = sig_handler;
|
||||
|
||||
if ((sigaction(SIGUSR1, &sa, NULL) == 0) &&
|
||||
(sigaction(SIGUSR2, &sa, NULL) == 0)) {
|
||||
tap->dev.link_state = &tap_link_state;
|
||||
}
|
||||
|
||||
tap->dev.overhead = 0;
|
||||
tap->fd = tap_open(name);
|
||||
if (tap->fd < 0) {
|
||||
dbg("Tap creation failed.\n");
|
||||
pico_tap_destroy((struct pico_device *)tap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Host's mac address is generated * by the host kernel and is
|
||||
* retrieved via tap_get_mac().
|
||||
*/
|
||||
if (tap_get_mac(name, mac) < 0) {
|
||||
dbg("Tap mac query failed.\n");
|
||||
pico_tap_destroy((struct pico_device *)tap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* To act as a second endpoint in the same subnet, the picoTCP
|
||||
* app using the tap device must have a different mac address.
|
||||
* For simplicity, we just add 1 to the last byte of the linux
|
||||
* endpoint so the two addresses are consecutive.
|
||||
*/
|
||||
mac[5]++;
|
||||
|
||||
if( 0 != pico_device_init((struct pico_device *)tap, name, mac)) {
|
||||
dbg("Tap init failed.\n");
|
||||
pico_tap_destroy((struct pico_device *)tap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tap->dev.send = pico_tap_send;
|
||||
tap->dev.poll = pico_tap_poll;
|
||||
tap->dev.destroy = pico_tap_destroy;
|
||||
dbg("Device %s created.\n", tap->dev.name);
|
||||
return (struct pico_device *)tap;
|
||||
}
|
||||
|
||||
15
kernel/picotcp/modules/pico_dev_tap.h
Normal file
15
kernel/picotcp/modules/pico_dev_tap.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_TAP
|
||||
#define INCLUDE_PICO_TAP
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
void pico_tap_destroy(struct pico_device *tap);
|
||||
struct pico_device *pico_tap_create(char *name);
|
||||
|
||||
#endif
|
||||
|
||||
1101
kernel/picotcp/modules/pico_dev_tap_windows.c
Normal file
1101
kernel/picotcp/modules/pico_dev_tap_windows.c
Normal file
File diff suppressed because it is too large
Load Diff
17
kernel/picotcp/modules/pico_dev_tap_windows.h
Normal file
17
kernel/picotcp/modules/pico_dev_tap_windows.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_TAP
|
||||
#define INCLUDE_PICO_TAP
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
/* will look for the first TAP device available, and use it */
|
||||
struct pico_device *pico_tap_create(char *name, uint8_t *mac);
|
||||
/* TODO: not implemented yet */
|
||||
/* void pico_tap_destroy(struct pico_device *null); */
|
||||
|
||||
#endif
|
||||
|
||||
89
kernel/picotcp/modules/pico_dev_tap_windows_private.h
Normal file
89
kernel/picotcp/modules/pico_dev_tap_windows_private.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Maxime Vincent
|
||||
Based on the OpenVPN tun.c driver, under GPL
|
||||
|
||||
NOTES: This is the Windows-only driver, a Linux-equivalent is available, too
|
||||
You need to have an OpenVPN TUN/TAP network adapter installed, first
|
||||
This driver is barely working:
|
||||
* Only TAP-mode is supported (TUN is not)
|
||||
* it will simply open the first TAP device it can find
|
||||
* there is memory being allocated that's never freed
|
||||
* there is no destroy function, yet
|
||||
* it has only been tested on a Windows 7 machine
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef __PICO_DEV_TAP_WINDOWS_PRIVATE_H
|
||||
#define __PICO_DEV_TAP_WINDOWS_PRIVATE_H
|
||||
|
||||
/* Extra defines (vnz) */
|
||||
#define TAP_WIN_COMPONENT_ID "tap0901"
|
||||
#define TAP_WIN_MIN_MAJOR 9
|
||||
#define TAP_WIN_MIN_MINOR 9
|
||||
#define PACKAGE_NAME "PicoTCP WinTAP"
|
||||
|
||||
/* Extra structs */
|
||||
struct tap_reg
|
||||
{
|
||||
const char *guid;
|
||||
struct tap_reg *next;
|
||||
};
|
||||
|
||||
struct panel_reg
|
||||
{
|
||||
const char *name;
|
||||
const char *guid;
|
||||
struct panel_reg *next;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* =============
|
||||
* TAP IOCTLs
|
||||
* =============
|
||||
*/
|
||||
|
||||
#define TAP_WIN_CONTROL_CODE(request, method) \
|
||||
CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
|
||||
|
||||
/* Present in 8.1 */
|
||||
|
||||
#define TAP_WIN_IOCTL_GET_MAC TAP_WIN_CONTROL_CODE (1, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_VERSION TAP_WIN_CONTROL_CODE (2, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_MTU TAP_WIN_CONTROL_CODE (3, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_INFO TAP_WIN_CONTROL_CODE (4, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_SET_MEDIA_STATUS TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED)
|
||||
|
||||
/* Added in 8.2 */
|
||||
|
||||
/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */
|
||||
#define TAP_WIN_IOCTL_CONFIG_TUN TAP_WIN_CONTROL_CODE (10, METHOD_BUFFERED)
|
||||
|
||||
/*
|
||||
* =================
|
||||
* Registry keys
|
||||
* =================
|
||||
*/
|
||||
|
||||
#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
|
||||
#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
|
||||
/*
|
||||
* ======================
|
||||
* Filesystem prefixes
|
||||
* ======================
|
||||
*/
|
||||
|
||||
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
|
||||
#define SYSDEVICEDIR "\\Device\\"
|
||||
#define USERDEVICEDIR "\\DosDevices\\Global\\"
|
||||
#define TAP_WIN_SUFFIX ".tap"
|
||||
|
||||
#endif
|
||||
110
kernel/picotcp/modules/pico_dev_tun.c
Normal file
110
kernel/picotcp/modules/pico_dev_tun.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_tun.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
#include <sys/poll.h>
|
||||
|
||||
struct pico_device_tun {
|
||||
struct pico_device dev;
|
||||
int fd;
|
||||
};
|
||||
|
||||
#define TUN_MTU 2048
|
||||
|
||||
static int pico_tun_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
struct pico_device_tun *tun = (struct pico_device_tun *) dev;
|
||||
return (int)write(tun->fd, buf, (uint32_t)len);
|
||||
}
|
||||
|
||||
static int pico_tun_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct pico_device_tun *tun = (struct pico_device_tun *) dev;
|
||||
struct pollfd pfd;
|
||||
unsigned char buf[TUN_MTU];
|
||||
int len;
|
||||
pfd.fd = tun->fd;
|
||||
pfd.events = POLLIN;
|
||||
do {
|
||||
if (poll(&pfd, 1, 0) <= 0)
|
||||
return loop_score;
|
||||
|
||||
len = (int)read(tun->fd, buf, TUN_MTU);
|
||||
if (len > 0) {
|
||||
loop_score--;
|
||||
pico_stack_recv(dev, buf, (uint32_t)len);
|
||||
}
|
||||
} while(loop_score > 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Public interface: create/destroy. */
|
||||
|
||||
void pico_tun_destroy(struct pico_device *dev)
|
||||
{
|
||||
struct pico_device_tun *tun = (struct pico_device_tun *) dev;
|
||||
if(tun->fd > 0)
|
||||
close(tun->fd);
|
||||
}
|
||||
|
||||
|
||||
static int tun_open(char *name)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int tun_fd;
|
||||
if((tun_fd = open("/dev/net/tun", O_RDWR)) < 0) {
|
||||
return(-1);
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
||||
strncpy(ifr.ifr_name, name, IFNAMSIZ);
|
||||
if(ioctl(tun_fd, TUNSETIFF, &ifr) < 0) {
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return tun_fd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct pico_device *pico_tun_create(char *name)
|
||||
{
|
||||
struct pico_device_tun *tun = PICO_ZALLOC(sizeof(struct pico_device_tun));
|
||||
|
||||
if (!tun)
|
||||
return NULL;
|
||||
|
||||
if( 0 != pico_device_init((struct pico_device *)tun, name, NULL)) {
|
||||
dbg("Tun init failed.\n");
|
||||
pico_tun_destroy((struct pico_device *)tun);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tun->dev.overhead = 0;
|
||||
tun->fd = tun_open(name);
|
||||
if (tun->fd < 0) {
|
||||
dbg("Tun creation failed.\n");
|
||||
pico_tun_destroy((struct pico_device *)tun);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tun->dev.send = pico_tun_send;
|
||||
tun->dev.poll = pico_tun_poll;
|
||||
tun->dev.destroy = pico_tun_destroy;
|
||||
dbg("Device %s created.\n", tun->dev.name);
|
||||
return (struct pico_device *)tun;
|
||||
}
|
||||
|
||||
15
kernel/picotcp/modules/pico_dev_tun.h
Normal file
15
kernel/picotcp/modules/pico_dev_tun.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_TUN
|
||||
#define INCLUDE_PICO_TUN
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
void pico_tun_destroy(struct pico_device *tun);
|
||||
struct pico_device *pico_tun_create(char *name);
|
||||
|
||||
#endif
|
||||
|
||||
122
kernel/picotcp/modules/pico_dev_vde.c
Normal file
122
kernel/picotcp/modules/pico_dev_vde.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <libvdeplug.h>
|
||||
#endif
|
||||
#include "pico_device.h"
|
||||
#include "pico_dev_vde.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
#include <sys/poll.h>
|
||||
|
||||
struct pico_device_vde {
|
||||
struct pico_device dev;
|
||||
char *sock;
|
||||
VDECONN *conn;
|
||||
uint32_t counter_in;
|
||||
uint32_t counter_out;
|
||||
uint32_t lost_in;
|
||||
uint32_t lost_out;
|
||||
};
|
||||
|
||||
#define VDE_MTU 65536
|
||||
|
||||
/* Mockables */
|
||||
#if defined UNIT_TEST
|
||||
# define MOCKABLE __attribute__((weak))
|
||||
#else
|
||||
# define MOCKABLE
|
||||
#endif
|
||||
|
||||
static int pico_vde_send(struct pico_device *dev, void *buf, int len)
|
||||
{
|
||||
struct pico_device_vde *vde = (struct pico_device_vde *) dev;
|
||||
/* dbg("[%s] send %d bytes.\n", dev->name, len); */
|
||||
if ((vde->lost_out == 0) || ((pico_rand() % 100) > vde->lost_out))
|
||||
return (int)vde_send(vde->conn, buf, (uint32_t)len, 0);
|
||||
else
|
||||
return len; /* Silently discarded "on the wire" */
|
||||
|
||||
}
|
||||
|
||||
static int pico_vde_poll(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct pico_device_vde *vde = (struct pico_device_vde *) dev;
|
||||
struct pollfd pfd;
|
||||
unsigned char buf[VDE_MTU];
|
||||
int len;
|
||||
pfd.fd = vde_datafd(vde->conn);
|
||||
pfd.events = POLLIN;
|
||||
do {
|
||||
if (poll(&pfd, 1, 0) <= 0)
|
||||
return loop_score;
|
||||
|
||||
len = (int)vde_recv(vde->conn, buf, VDE_MTU, 0);
|
||||
if (len > 0) {
|
||||
/* dbg("Received pkt.\n"); */
|
||||
if ((vde->lost_in == 0) || ((pico_rand() % 100) > vde->lost_in)) {
|
||||
loop_score--;
|
||||
pico_stack_recv(dev, buf, (uint32_t)len);
|
||||
}
|
||||
}
|
||||
} while(loop_score > 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Public interface: create/destroy. */
|
||||
|
||||
void pico_vde_destroy(struct pico_device *dev)
|
||||
{
|
||||
struct pico_device_vde *vde = (struct pico_device_vde *) dev;
|
||||
vde_close(vde->conn);
|
||||
usleep(100000);
|
||||
sync();
|
||||
}
|
||||
|
||||
void pico_vde_set_packetloss(struct pico_device *dev, uint32_t in_pct, uint32_t out_pct)
|
||||
{
|
||||
struct pico_device_vde *vde = (struct pico_device_vde *) dev;
|
||||
vde->lost_in = in_pct;
|
||||
vde->lost_out = out_pct;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct pico_device *MOCKABLE pico_vde_create(char *sock, char *name, uint8_t *mac)
|
||||
{
|
||||
struct pico_device_vde *vde = PICO_ZALLOC(sizeof(struct pico_device_vde));
|
||||
struct vde_open_args open_args = {
|
||||
.mode = 0700
|
||||
};
|
||||
char vdename[] = "picotcp";
|
||||
|
||||
if (!vde)
|
||||
return NULL;
|
||||
|
||||
if( 0 != pico_device_init((struct pico_device *)vde, name, mac)) {
|
||||
dbg ("Vde init failed.\n");
|
||||
pico_vde_destroy((struct pico_device *)vde);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vde->dev.overhead = 0;
|
||||
vde->sock = PICO_ZALLOC(strlen(sock) + 1);
|
||||
memcpy(vde->sock, sock, strlen(sock));
|
||||
vde->conn = vde_open(sock, vdename, &open_args);
|
||||
if (!vde->conn) {
|
||||
pico_vde_destroy((struct pico_device *)vde);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vde->dev.send = pico_vde_send;
|
||||
vde->dev.poll = pico_vde_poll;
|
||||
vde->dev.destroy = pico_vde_destroy;
|
||||
dbg("Device %s created.\n", vde->dev.name);
|
||||
return (struct pico_device *)vde;
|
||||
}
|
||||
|
||||
18
kernel/picotcp/modules/pico_dev_vde.h
Normal file
18
kernel/picotcp/modules/pico_dev_vde.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_VDE
|
||||
#define INCLUDE_PICO_VDE
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
#include <libvdeplug.h>
|
||||
|
||||
void pico_vde_destroy(struct pico_device *vde);
|
||||
struct pico_device *pico_vde_create(char *sock, char *name, uint8_t *mac);
|
||||
void pico_vde_set_packetloss(struct pico_device *dev, uint32_t in_pct, uint32_t out_pct);
|
||||
|
||||
#endif
|
||||
|
||||
1074
kernel/picotcp/modules/pico_dhcp_client.c
Normal file
1074
kernel/picotcp/modules/pico_dhcp_client.c
Normal file
File diff suppressed because it is too large
Load Diff
32
kernel/picotcp/modules/pico_dhcp_client.h
Normal file
32
kernel/picotcp/modules/pico_dhcp_client.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_DHCP_CLIENT
|
||||
#define INCLUDE_PICO_DHCP_CLIENT
|
||||
#include "pico_defines.h"
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
#include "pico_dhcp_common.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
|
||||
int pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void*cli, int code), uint32_t *xid);
|
||||
void *pico_dhcp_get_identifier(uint32_t xid);
|
||||
struct pico_ip4 pico_dhcp_get_address(void *cli);
|
||||
struct pico_ip4 pico_dhcp_get_gateway(void *cli);
|
||||
struct pico_ip4 pico_dhcp_get_netmask(void *cli);
|
||||
struct pico_ip4 pico_dhcp_get_nameserver(void*cli, int index);
|
||||
int pico_dhcp_client_abort(uint32_t xid);
|
||||
char *pico_dhcp_get_hostname(void);
|
||||
char *pico_dhcp_get_domain(void);
|
||||
|
||||
/* possible codes for the callback */
|
||||
#define PICO_DHCP_SUCCESS 0
|
||||
#define PICO_DHCP_ERROR 1
|
||||
#define PICO_DHCP_RESET 2
|
||||
|
||||
#endif
|
||||
#endif
|
||||
190
kernel/picotcp/modules/pico_dhcp_common.c
Normal file
190
kernel/picotcp/modules/pico_dhcp_common.c
Normal file
@ -0,0 +1,190 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Frederik Van Slycken
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_dhcp_common.h"
|
||||
|
||||
#if defined (PICO_SUPPORT_DHCPC) || defined (PICO_SUPPORT_DHCPD)
|
||||
/* pico_dhcp_are_options_valid needs to be called first to prevent illegal memory access */
|
||||
/* The argument pointer is moved forward to the next option */
|
||||
struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr)
|
||||
{
|
||||
uint8_t **p = (uint8_t **)ptr;
|
||||
struct pico_dhcp_opt *opt = *ptr;
|
||||
|
||||
if (opt->code == PICO_DHCP_OPT_END)
|
||||
return NULL;
|
||||
|
||||
if (opt->code == PICO_DHCP_OPT_PAD) {
|
||||
*p += 1;
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
*p += (opt->len + 2); /* (len + 2) to account for code and len octet */
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len)
|
||||
{
|
||||
uint8_t optlen = 0, *p = ptr;
|
||||
|
||||
while (len > 0) {
|
||||
switch (*p)
|
||||
{
|
||||
case PICO_DHCP_OPT_END:
|
||||
return 1;
|
||||
|
||||
case PICO_DHCP_OPT_PAD:
|
||||
p++;
|
||||
len--;
|
||||
break;
|
||||
|
||||
default:
|
||||
p++; /* move pointer from code octet to len octet */
|
||||
len--;
|
||||
if ((len <= 0) || (len - (*p + 1) < 0)) /* (*p + 1) to account for len octet */
|
||||
return 0;
|
||||
|
||||
optlen = *p;
|
||||
p += optlen + 1;
|
||||
len -= optlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: netmask */
|
||||
opt->code = PICO_DHCP_OPT_NETMASK;
|
||||
opt->len = PICO_DHCP_OPTLEN_NETMASK - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.netmask.ip = *ip;
|
||||
return PICO_DHCP_OPTLEN_NETMASK;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: router */
|
||||
opt->code = PICO_DHCP_OPT_ROUTER;
|
||||
opt->len = PICO_DHCP_OPTLEN_ROUTER - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.router.ip = *ip;
|
||||
return PICO_DHCP_OPTLEN_ROUTER;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: dns */
|
||||
opt->code = PICO_DHCP_OPT_DNS;
|
||||
opt->len = PICO_DHCP_OPTLEN_DNS - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.dns1.ip = *ip;
|
||||
return PICO_DHCP_OPTLEN_DNS;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: broadcast */
|
||||
opt->code = PICO_DHCP_OPT_BROADCAST;
|
||||
opt->len = PICO_DHCP_OPTLEN_BROADCAST - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.broadcast.ip = *ip;
|
||||
return PICO_DHCP_OPTLEN_BROADCAST;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: request IP address */
|
||||
opt->code = PICO_DHCP_OPT_REQIP;
|
||||
opt->len = PICO_DHCP_OPTLEN_REQIP - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.req_ip.ip = *ip;
|
||||
return PICO_DHCP_OPTLEN_REQIP;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: lease time */
|
||||
opt->code = PICO_DHCP_OPT_LEASETIME;
|
||||
opt->len = PICO_DHCP_OPTLEN_LEASETIME - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.lease_time.time = time;
|
||||
return PICO_DHCP_OPTLEN_LEASETIME;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: message type */
|
||||
opt->code = PICO_DHCP_OPT_MSGTYPE;
|
||||
opt->len = PICO_DHCP_OPTLEN_MSGTYPE - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.msg_type.type = type;
|
||||
return PICO_DHCP_OPTLEN_MSGTYPE;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: server identifier */
|
||||
opt->code = PICO_DHCP_OPT_SERVERID;
|
||||
opt->len = PICO_DHCP_OPTLEN_SERVERID - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.server_id.ip = *ip;
|
||||
return PICO_DHCP_OPTLEN_SERVERID;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_paramlist(void *ptr)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
uint8_t *param_code = &(opt->ext.param_list.code[0]);
|
||||
|
||||
/* option: parameter list */
|
||||
opt->code = PICO_DHCP_OPT_PARAMLIST;
|
||||
opt->len = PICO_DHCP_OPTLEN_PARAMLIST - PICO_DHCP_OPTLEN_HDR;
|
||||
param_code[0] = PICO_DHCP_OPT_NETMASK;
|
||||
param_code[1] = PICO_DHCP_OPT_TIME;
|
||||
param_code[2] = PICO_DHCP_OPT_ROUTER;
|
||||
param_code[3] = PICO_DHCP_OPT_HOSTNAME;
|
||||
param_code[4] = PICO_DHCP_OPT_RENEWALTIME;
|
||||
param_code[5] = PICO_DHCP_OPT_REBINDINGTIME;
|
||||
param_code[6] = PICO_DHCP_OPT_DNS;
|
||||
return PICO_DHCP_OPTLEN_PARAMLIST;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
|
||||
|
||||
/* option: maximum message size */
|
||||
opt->code = PICO_DHCP_OPT_MAXMSGSIZE;
|
||||
opt->len = PICO_DHCP_OPTLEN_MAXMSGSIZE - PICO_DHCP_OPTLEN_HDR;
|
||||
opt->ext.max_msg_size.size = short_be(size);
|
||||
return PICO_DHCP_OPTLEN_MAXMSGSIZE;
|
||||
}
|
||||
|
||||
uint8_t pico_dhcp_opt_end(void *ptr)
|
||||
{
|
||||
uint8_t *opt = (uint8_t *)ptr;
|
||||
|
||||
/* option: end of options */
|
||||
*opt = PICO_DHCP_OPT_END;
|
||||
return PICO_DHCP_OPTLEN_END;
|
||||
}
|
||||
|
||||
#endif
|
||||
191
kernel/picotcp/modules/pico_dhcp_common.h
Normal file
191
kernel/picotcp/modules/pico_dhcp_common.h
Normal file
@ -0,0 +1,191 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_DHCP_COMMON
|
||||
#define INCLUDE_PICO_DHCP_COMMON
|
||||
#include "pico_config.h"
|
||||
#include "pico_addressing.h"
|
||||
|
||||
#define PICO_DHCPD_PORT (short_be(67))
|
||||
#define PICO_DHCP_CLIENT_PORT (short_be(68))
|
||||
#define PICO_DHCPD_MAGIC_COOKIE (long_be(0x63825363))
|
||||
#define PICO_DHCP_HTYPE_ETH 1
|
||||
|
||||
/* Macro to get DHCP option field */
|
||||
#define DHCP_OPT(hdr, off) ((struct pico_dhcp_opt *)(((uint8_t *)hdr) + sizeof(struct pico_dhcp_hdr) + off))
|
||||
|
||||
/* flags */
|
||||
#define PICO_DHCP_FLAG_BROADCAST 0x8000
|
||||
|
||||
/* options */
|
||||
#define PICO_DHCP_OPT_PAD 0x00
|
||||
#define PICO_DHCP_OPT_NETMASK 0x01
|
||||
#define PICO_DHCP_OPT_TIME 0x02
|
||||
#define PICO_DHCP_OPT_ROUTER 0x03
|
||||
#define PICO_DHCP_OPT_DNS 0x06
|
||||
#define PICO_DHCP_OPT_HOSTNAME 0x0c
|
||||
#define PICO_DHCP_OPT_DOMAINNAME 0x0f
|
||||
#define PICO_DHCP_OPT_MTU 0x1a
|
||||
#define PICO_DHCP_OPT_BROADCAST 0x1c
|
||||
#define PICO_DHCP_OPT_NETBIOSNS 0x2c
|
||||
#define PICO_DHCP_OPT_NETBIOSSCOPE 0x2f
|
||||
#define PICO_DHCP_OPT_REQIP 0x32
|
||||
#define PICO_DHCP_OPT_LEASETIME 0x33
|
||||
#define PICO_DHCP_OPT_OPTOVERLOAD 0x34
|
||||
#define PICO_DHCP_OPT_MSGTYPE 0x35
|
||||
#define PICO_DHCP_OPT_SERVERID 0x36
|
||||
#define PICO_DHCP_OPT_PARAMLIST 0x37
|
||||
#define PICO_DHCP_OPT_MESSAGE 0x38
|
||||
#define PICO_DHCP_OPT_MAXMSGSIZE 0x39
|
||||
#define PICO_DHCP_OPT_RENEWALTIME 0x3a
|
||||
#define PICO_DHCP_OPT_REBINDINGTIME 0x3b
|
||||
#define PICO_DHCP_OPT_VENDORID 0x3c
|
||||
#define PICO_DHCP_OPT_CLIENTID 0x3d
|
||||
#define PICO_DHCP_OPT_DOMAINSEARCH 0x77
|
||||
#define PICO_DHCP_OPT_STATICROUTE 0x79
|
||||
#define PICO_DHCP_OPT_END 0xFF
|
||||
|
||||
/* options len */
|
||||
#define PICO_DHCP_OPTLEN_HDR 2 /* account for code and len field */
|
||||
#define PICO_DHCP_OPTLEN_NETMASK 6
|
||||
#define PICO_DHCP_OPTLEN_ROUTER 6
|
||||
#define PICO_DHCP_OPTLEN_DNS 6
|
||||
#define PICO_DHCP_OPTLEN_BROADCAST 6
|
||||
#define PICO_DHCP_OPTLEN_REQIP 6
|
||||
#define PICO_DHCP_OPTLEN_LEASETIME 6
|
||||
#define PICO_DHCP_OPTLEN_OPTOVERLOAD 3
|
||||
#define PICO_DHCP_OPTLEN_MSGTYPE 3
|
||||
#define PICO_DHCP_OPTLEN_SERVERID 6
|
||||
#define PICO_DHCP_OPTLEN_PARAMLIST 9 /* PicoTCP specific */
|
||||
#define PICO_DHCP_OPTLEN_MAXMSGSIZE 4
|
||||
#define PICO_DHCP_OPTLEN_RENEWALTIME 6
|
||||
#define PICO_DHCP_OPTLEN_REBINDINGTIME 6
|
||||
#define PICO_DHCP_OPTLEN_END 1
|
||||
|
||||
/* op codes */
|
||||
#define PICO_DHCP_OP_REQUEST 1
|
||||
#define PICO_DHCP_OP_REPLY 2
|
||||
|
||||
/* rfc message types */
|
||||
#define PICO_DHCP_MSG_DISCOVER 1
|
||||
#define PICO_DHCP_MSG_OFFER 2
|
||||
#define PICO_DHCP_MSG_REQUEST 3
|
||||
#define PICO_DHCP_MSG_DECLINE 4
|
||||
#define PICO_DHCP_MSG_ACK 5
|
||||
#define PICO_DHCP_MSG_NAK 6
|
||||
#define PICO_DHCP_MSG_RELEASE 7
|
||||
#define PICO_DHCP_MSG_INFORM 8
|
||||
|
||||
/* custom message types */
|
||||
#define PICO_DHCP_EVENT_T1 9
|
||||
#define PICO_DHCP_EVENT_T2 10
|
||||
#define PICO_DHCP_EVENT_LEASE 11
|
||||
#define PICO_DHCP_EVENT_RETRANSMIT 12
|
||||
#define PICO_DHCP_EVENT_NONE 0xff
|
||||
|
||||
PACKED_STRUCT_DEF pico_dhcp_hdr
|
||||
{
|
||||
uint8_t op;
|
||||
uint8_t htype;
|
||||
uint8_t hlen;
|
||||
uint8_t hops; /* zero */
|
||||
uint32_t xid; /* store this in the request */
|
||||
uint16_t secs; /* ignore */
|
||||
uint16_t flags;
|
||||
uint32_t ciaddr; /* client address - if asking for renewal */
|
||||
uint32_t yiaddr; /* your address (client) */
|
||||
uint32_t siaddr; /* dhcp offered address */
|
||||
uint32_t giaddr; /* relay agent, bootp. */
|
||||
uint8_t hwaddr[6];
|
||||
uint8_t hwaddr_padding[10];
|
||||
char hostname[64];
|
||||
char bootp_filename[128];
|
||||
uint32_t dhcp_magic;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_dhcp_opt
|
||||
{
|
||||
uint8_t code;
|
||||
uint8_t len;
|
||||
PACKED_UNION_DEF dhcp_opt_ext_u {
|
||||
PEDANTIC_STRUCT_DEF netmask_s {
|
||||
struct pico_ip4 ip;
|
||||
} netmask;
|
||||
PEDANTIC_STRUCT_DEF router_s {
|
||||
struct pico_ip4 ip;
|
||||
} router;
|
||||
PEDANTIC_STRUCT_DEF dns_s {
|
||||
struct pico_ip4 ip;
|
||||
} dns1;
|
||||
struct dns_s dns2;
|
||||
PEDANTIC_STRUCT_DEF broadcast_s {
|
||||
struct pico_ip4 ip;
|
||||
} broadcast;
|
||||
PEDANTIC_STRUCT_DEF req_ip_s {
|
||||
struct pico_ip4 ip;
|
||||
} req_ip;
|
||||
PEDANTIC_STRUCT_DEF lease_time_s {
|
||||
uint32_t time;
|
||||
} lease_time;
|
||||
PEDANTIC_STRUCT_DEF opt_overload_s {
|
||||
uint8_t value;
|
||||
} opt_overload;
|
||||
PEDANTIC_STRUCT_DEF tftp_server_s {
|
||||
char name[1];
|
||||
} tftp_server;
|
||||
PEDANTIC_STRUCT_DEF bootfile_s {
|
||||
char name[1];
|
||||
} bootfile;
|
||||
PEDANTIC_STRUCT_DEF msg_type_s {
|
||||
uint8_t type;
|
||||
} msg_type;
|
||||
PEDANTIC_STRUCT_DEF server_id_s {
|
||||
struct pico_ip4 ip;
|
||||
} server_id;
|
||||
PEDANTIC_STRUCT_DEF param_list_s {
|
||||
uint8_t code[1];
|
||||
} param_list;
|
||||
PEDANTIC_STRUCT_DEF message_s {
|
||||
char error[1];
|
||||
} message;
|
||||
PEDANTIC_STRUCT_DEF max_msg_size_s {
|
||||
uint16_t size;
|
||||
} max_msg_size;
|
||||
PEDANTIC_STRUCT_DEF renewal_time_s {
|
||||
uint32_t time;
|
||||
} renewal_time;
|
||||
PEDANTIC_STRUCT_DEF rebinding_time_s {
|
||||
uint32_t time;
|
||||
} rebinding_time;
|
||||
PEDANTIC_STRUCT_DEF vendor_id_s {
|
||||
uint8_t id[1];
|
||||
} vendor_id;
|
||||
PEDANTIC_STRUCT_DEF client_id_s {
|
||||
uint8_t id[1];
|
||||
} client_id;
|
||||
PEDANTIC_STRUCT_DEF text_s {
|
||||
char txt[1];
|
||||
} string;
|
||||
} ext;
|
||||
};
|
||||
|
||||
uint8_t dhcp_get_next_option(uint8_t *begin, uint8_t *data, int *len, uint8_t **nextopt);
|
||||
struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr);
|
||||
uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len);
|
||||
|
||||
uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip);
|
||||
uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip);
|
||||
uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip);
|
||||
uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip);
|
||||
uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip);
|
||||
uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time);
|
||||
uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type);
|
||||
uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip);
|
||||
uint8_t pico_dhcp_opt_paramlist(void *ptr);
|
||||
uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size);
|
||||
uint8_t pico_dhcp_opt_end(void *ptr);
|
||||
#endif
|
||||
426
kernel/picotcp/modules/pico_dhcp_server.c
Normal file
426
kernel/picotcp/modules/pico_dhcp_server.c
Normal file
@ -0,0 +1,426 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
|
||||
Authors: Frederik Van Slycken, Kristof Roelants
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_dhcp_server.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_udp.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_arp.h"
|
||||
|
||||
#if (defined PICO_SUPPORT_DHCPD && defined PICO_SUPPORT_UDP)
|
||||
|
||||
#ifdef DEBUG_DHCP_SERVER
|
||||
#define dhcps_dbg dbg
|
||||
#else
|
||||
#define dhcps_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/* default configurations */
|
||||
#define DHCP_SERVER_OPENDNS long_be(0xd043dede) /* OpenDNS DNS server 208.67.222.222 */
|
||||
#define DHCP_SERVER_POOL_START long_be(0x00000064)
|
||||
#define DHCP_SERVER_POOL_END long_be(0x000000fe)
|
||||
#define DHCP_SERVER_LEASE_TIME long_be(0x00000078)
|
||||
|
||||
/* maximum size of a DHCP message */
|
||||
#define DHCP_SERVER_MAXMSGSIZE (PICO_IP_MRU - sizeof(struct pico_ipv4_hdr) - sizeof(struct pico_udp_hdr))
|
||||
|
||||
enum dhcp_server_state {
|
||||
PICO_DHCP_STATE_DISCOVER = 0,
|
||||
PICO_DHCP_STATE_OFFER,
|
||||
PICO_DHCP_STATE_REQUEST,
|
||||
PICO_DHCP_STATE_BOUND,
|
||||
PICO_DHCP_STATE_RENEWING
|
||||
};
|
||||
|
||||
struct pico_dhcp_server_negotiation {
|
||||
uint32_t xid;
|
||||
enum dhcp_server_state state;
|
||||
struct pico_dhcp_server_setting *dhcps;
|
||||
struct pico_ip4 ciaddr;
|
||||
struct pico_eth hwaddr;
|
||||
uint8_t bcast;
|
||||
};
|
||||
|
||||
static inline int ip_address_is_in_dhcp_range(struct pico_dhcp_server_negotiation *n, uint32_t x)
|
||||
{
|
||||
uint32_t ip_hostendian = long_be(x);
|
||||
if (ip_hostendian < long_be(n->dhcps->pool_start))
|
||||
return 0;
|
||||
|
||||
if (ip_hostendian > long_be(n->dhcps->pool_end))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void pico_dhcpd_wakeup(uint16_t ev, struct pico_socket *s);
|
||||
|
||||
static int dhcp_settings_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_dhcp_server_setting *a = ka, *b = kb;
|
||||
if (a->dev == b->dev)
|
||||
return 0;
|
||||
|
||||
return (a->dev < b->dev) ? (-1) : (1);
|
||||
}
|
||||
static PICO_TREE_DECLARE(DHCPSettings, dhcp_settings_cmp);
|
||||
|
||||
static int dhcp_negotiations_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_dhcp_server_negotiation *a = ka, *b = kb;
|
||||
if (a->xid == b->xid)
|
||||
return 0;
|
||||
|
||||
return (a->xid < b->xid) ? (-1) : (1);
|
||||
}
|
||||
static PICO_TREE_DECLARE(DHCPNegotiations, dhcp_negotiations_cmp);
|
||||
|
||||
|
||||
static inline void dhcps_set_default_pool_start_if_not_provided(struct pico_dhcp_server_setting *dhcps)
|
||||
{
|
||||
if (!dhcps->pool_start)
|
||||
dhcps->pool_start = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_START;
|
||||
}
|
||||
|
||||
static inline void dhcps_set_default_pool_end_if_not_provided(struct pico_dhcp_server_setting *dhcps)
|
||||
{
|
||||
if (!dhcps->pool_end)
|
||||
dhcps->pool_end = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_END;
|
||||
}
|
||||
static inline void dhcps_set_default_lease_time_if_not_provided(struct pico_dhcp_server_setting *dhcps)
|
||||
{
|
||||
if (!dhcps->lease_time)
|
||||
dhcps->lease_time = DHCP_SERVER_LEASE_TIME;
|
||||
}
|
||||
|
||||
static inline struct pico_dhcp_server_setting *dhcps_try_open_socket(struct pico_dhcp_server_setting *dhcps)
|
||||
{
|
||||
uint16_t port = PICO_DHCPD_PORT;
|
||||
dhcps->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup);
|
||||
if (!dhcps->s) {
|
||||
dhcps_dbg("DHCP server ERROR: failure opening socket (%s)\n", strerror(pico_err));
|
||||
PICO_FREE(dhcps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pico_socket_bind(dhcps->s, &dhcps->server_ip, &port) < 0) {
|
||||
dhcps_dbg("DHCP server ERROR: failure binding socket (%s)\n", strerror(pico_err));
|
||||
PICO_FREE(dhcps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pico_tree_insert(&DHCPSettings, dhcps)) {
|
||||
dhcps_dbg("DHCP server ERROR: could not insert settings in tree\n");
|
||||
PICO_FREE(dhcps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dhcps;
|
||||
}
|
||||
|
||||
static struct pico_dhcp_server_setting *pico_dhcp_server_add_setting(struct pico_dhcp_server_setting *setting)
|
||||
{
|
||||
struct pico_dhcp_server_setting *dhcps = NULL, *found = NULL, test = {
|
||||
0
|
||||
};
|
||||
struct pico_ipv4_link *link = NULL;
|
||||
|
||||
link = pico_ipv4_link_get(&setting->server_ip);
|
||||
if (!link) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
test.dev = setting->dev;
|
||||
found = pico_tree_findKey(&DHCPSettings, &test);
|
||||
if (found) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dhcps = PICO_ZALLOC(sizeof(struct pico_dhcp_server_setting));
|
||||
if (!dhcps) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dhcps->lease_time = setting->lease_time;
|
||||
dhcps->pool_start = setting->pool_start;
|
||||
dhcps->pool_next = setting->pool_next;
|
||||
dhcps->pool_end = setting->pool_end;
|
||||
dhcps->dev = link->dev;
|
||||
dhcps->server_ip = link->address;
|
||||
dhcps->netmask = link->netmask;
|
||||
|
||||
/* default values if not provided */
|
||||
dhcps_set_default_lease_time_if_not_provided(dhcps);
|
||||
dhcps_set_default_pool_end_if_not_provided(dhcps);
|
||||
dhcps_set_default_pool_start_if_not_provided(dhcps);
|
||||
|
||||
dhcps->pool_next = dhcps->pool_start;
|
||||
|
||||
return dhcps_try_open_socket(dhcps);
|
||||
|
||||
}
|
||||
|
||||
static struct pico_dhcp_server_negotiation *pico_dhcp_server_find_negotiation(uint32_t xid)
|
||||
{
|
||||
struct pico_dhcp_server_negotiation test = {
|
||||
0
|
||||
}, *found = NULL;
|
||||
|
||||
test.xid = xid;
|
||||
found = pico_tree_findKey(&DHCPNegotiations, &test);
|
||||
if (found)
|
||||
return found;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void dhcp_negotiation_set_ciaddr(struct pico_dhcp_server_negotiation *dhcpn)
|
||||
{
|
||||
struct pico_ip4 *ciaddr = NULL;
|
||||
ciaddr = pico_arp_reverse_lookup(&dhcpn->hwaddr);
|
||||
if (!ciaddr) {
|
||||
dhcpn->ciaddr.addr = dhcpn->dhcps->pool_next;
|
||||
dhcpn->dhcps->pool_next = long_be(long_be(dhcpn->dhcps->pool_next) + 1);
|
||||
pico_arp_create_entry(dhcpn->hwaddr.addr, dhcpn->ciaddr, dhcpn->dhcps->dev);
|
||||
} else {
|
||||
dhcpn->ciaddr = *ciaddr;
|
||||
}
|
||||
}
|
||||
|
||||
static struct pico_dhcp_server_negotiation *pico_dhcp_server_add_negotiation(struct pico_device *dev, struct pico_dhcp_hdr *hdr)
|
||||
{
|
||||
struct pico_dhcp_server_negotiation *dhcpn = NULL;
|
||||
struct pico_dhcp_server_setting test = {
|
||||
0
|
||||
};
|
||||
|
||||
if (pico_dhcp_server_find_negotiation(hdr->xid))
|
||||
return NULL;
|
||||
|
||||
dhcpn = PICO_ZALLOC(sizeof(struct pico_dhcp_server_negotiation));
|
||||
if (!dhcpn) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dhcpn->xid = hdr->xid;
|
||||
dhcpn->state = PICO_DHCP_STATE_DISCOVER;
|
||||
dhcpn->bcast = ((short_be(hdr->flags) & PICO_DHCP_FLAG_BROADCAST) != 0) ? (1) : (0);
|
||||
memcpy(dhcpn->hwaddr.addr, hdr->hwaddr, PICO_SIZE_ETH);
|
||||
|
||||
test.dev = dev;
|
||||
dhcpn->dhcps = pico_tree_findKey(&DHCPSettings, &test);
|
||||
if (!dhcpn->dhcps) {
|
||||
dhcps_dbg("DHCP server WARNING: received DHCP message on unconfigured link %s\n", dev->name);
|
||||
PICO_FREE(dhcpn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dhcp_negotiation_set_ciaddr(dhcpn);
|
||||
if (pico_tree_insert(&DHCPNegotiations, dhcpn)) {
|
||||
dhcps_dbg("DHCP server ERROR: could not insert negotiations in tree\n");
|
||||
PICO_FREE(dhcpn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dhcpn;
|
||||
}
|
||||
|
||||
static void dhcpd_make_reply(struct pico_dhcp_server_negotiation *dhcpn, uint8_t msg_type)
|
||||
{
|
||||
int r = 0, optlen = 0, offset = 0;
|
||||
struct pico_ip4 broadcast = {
|
||||
0
|
||||
}, dns = {
|
||||
0
|
||||
}, destination = {
|
||||
.addr = 0xFFFFFFFF
|
||||
};
|
||||
struct pico_dhcp_hdr *hdr = NULL;
|
||||
|
||||
dns.addr = DHCP_SERVER_OPENDNS;
|
||||
broadcast.addr = dhcpn->dhcps->server_ip.addr | ~(dhcpn->dhcps->netmask.addr);
|
||||
|
||||
optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_SERVERID + PICO_DHCP_OPTLEN_LEASETIME + PICO_DHCP_OPTLEN_NETMASK + PICO_DHCP_OPTLEN_ROUTER
|
||||
+ PICO_DHCP_OPTLEN_BROADCAST + PICO_DHCP_OPTLEN_DNS + PICO_DHCP_OPTLEN_END;
|
||||
hdr = PICO_ZALLOC(sizeof(struct pico_dhcp_hdr) + (uint32_t)optlen);
|
||||
if (!hdr) {
|
||||
return;
|
||||
}
|
||||
|
||||
hdr->op = PICO_DHCP_OP_REPLY;
|
||||
hdr->htype = PICO_DHCP_HTYPE_ETH;
|
||||
hdr->hlen = PICO_SIZE_ETH;
|
||||
hdr->xid = dhcpn->xid;
|
||||
hdr->yiaddr = dhcpn->ciaddr.addr;
|
||||
hdr->siaddr = dhcpn->dhcps->server_ip.addr;
|
||||
hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
|
||||
memcpy(hdr->hwaddr, dhcpn->hwaddr.addr, PICO_SIZE_ETH);
|
||||
|
||||
/* options */
|
||||
offset += pico_dhcp_opt_msgtype(DHCP_OPT(hdr, offset), msg_type);
|
||||
offset += pico_dhcp_opt_serverid(DHCP_OPT(hdr, offset), &dhcpn->dhcps->server_ip);
|
||||
offset += pico_dhcp_opt_leasetime(DHCP_OPT(hdr, offset), dhcpn->dhcps->lease_time);
|
||||
offset += pico_dhcp_opt_netmask(DHCP_OPT(hdr, offset), &dhcpn->dhcps->netmask);
|
||||
offset += pico_dhcp_opt_router(DHCP_OPT(hdr, offset), &dhcpn->dhcps->server_ip);
|
||||
offset += pico_dhcp_opt_broadcast(DHCP_OPT(hdr, offset), &broadcast);
|
||||
offset += pico_dhcp_opt_dns(DHCP_OPT(hdr, offset), &dns);
|
||||
offset += pico_dhcp_opt_end(DHCP_OPT(hdr, offset));
|
||||
|
||||
if (dhcpn->bcast == 0)
|
||||
destination.addr = hdr->yiaddr;
|
||||
else {
|
||||
hdr->flags |= short_be(PICO_DHCP_FLAG_BROADCAST);
|
||||
destination.addr = broadcast.addr;
|
||||
}
|
||||
|
||||
r = pico_socket_sendto(dhcpn->dhcps->s, hdr, (int)(sizeof(struct pico_dhcp_hdr) + (uint32_t)optlen), &destination, PICO_DHCP_CLIENT_PORT);
|
||||
if (r < 0)
|
||||
dhcps_dbg("DHCP server WARNING: failure sending: %s!\n", strerror(pico_err));
|
||||
|
||||
PICO_FREE(hdr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void parse_opt_msgtype(struct pico_dhcp_opt *opt, uint8_t *msgtype)
|
||||
{
|
||||
if (opt->code == PICO_DHCP_OPT_MSGTYPE) {
|
||||
*msgtype = opt->ext.msg_type.type;
|
||||
dhcps_dbg("DHCP server: message type %u\n", *msgtype);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void parse_opt_reqip(struct pico_dhcp_opt *opt, struct pico_ip4 *reqip)
|
||||
{
|
||||
if (opt->code == PICO_DHCP_OPT_REQIP)
|
||||
reqip->addr = opt->ext.req_ip.ip.addr;
|
||||
}
|
||||
|
||||
static inline void parse_opt_serverid(struct pico_dhcp_opt *opt, struct pico_ip4 *serverid)
|
||||
{
|
||||
if (opt->code == PICO_DHCP_OPT_SERVERID)
|
||||
*serverid = opt->ext.server_id.ip;
|
||||
}
|
||||
|
||||
static inline void dhcps_make_reply_to_request_msg(struct pico_dhcp_server_negotiation *dhcpn, int bound_valid_flag)
|
||||
{
|
||||
if ((dhcpn->state == PICO_DHCP_STATE_BOUND) && bound_valid_flag)
|
||||
dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK);
|
||||
|
||||
if (dhcpn->state == PICO_DHCP_STATE_OFFER) {
|
||||
dhcpn->state = PICO_DHCP_STATE_BOUND;
|
||||
dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dhcps_make_reply_to_discover_or_request(struct pico_dhcp_server_negotiation *dhcpn, uint8_t msgtype, int bound_valid_flag)
|
||||
{
|
||||
if (PICO_DHCP_MSG_DISCOVER == msgtype) {
|
||||
dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_OFFER);
|
||||
dhcpn->state = PICO_DHCP_STATE_OFFER;
|
||||
} else if (PICO_DHCP_MSG_REQUEST == msgtype) {
|
||||
dhcps_make_reply_to_request_msg(dhcpn, bound_valid_flag);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dhcps_parse_options_loop(struct pico_dhcp_server_negotiation *dhcpn, struct pico_dhcp_hdr *hdr)
|
||||
{
|
||||
struct pico_dhcp_opt *opt = DHCP_OPT(hdr, 0);
|
||||
uint8_t msgtype = 0;
|
||||
struct pico_ip4 reqip = {
|
||||
0
|
||||
}, server_id = {
|
||||
0
|
||||
};
|
||||
|
||||
do {
|
||||
parse_opt_msgtype(opt, &msgtype);
|
||||
parse_opt_reqip(opt, &reqip);
|
||||
parse_opt_serverid(opt, &server_id);
|
||||
} while (pico_dhcp_next_option(&opt));
|
||||
dhcps_make_reply_to_discover_or_request(dhcpn, msgtype, (!reqip.addr) && (!server_id.addr) && (hdr->ciaddr == dhcpn->ciaddr.addr));
|
||||
}
|
||||
|
||||
static void pico_dhcp_server_recv(struct pico_socket *s, uint8_t *buf, uint32_t len)
|
||||
{
|
||||
int32_t optlen = (int32_t)(len - sizeof(struct pico_dhcp_hdr));
|
||||
struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
|
||||
struct pico_dhcp_server_negotiation *dhcpn = NULL;
|
||||
struct pico_device *dev = NULL;
|
||||
|
||||
if (!pico_dhcp_are_options_valid(DHCP_OPT(hdr, 0), optlen))
|
||||
return;
|
||||
|
||||
dev = pico_ipv4_link_find(&s->local_addr.ip4);
|
||||
dhcpn = pico_dhcp_server_find_negotiation(hdr->xid);
|
||||
if (!dhcpn)
|
||||
dhcpn = pico_dhcp_server_add_negotiation(dev, hdr);
|
||||
|
||||
if (!ip_address_is_in_dhcp_range(dhcpn, dhcpn->ciaddr.addr))
|
||||
return;
|
||||
|
||||
dhcps_parse_options_loop(dhcpn, hdr);
|
||||
|
||||
}
|
||||
|
||||
static void pico_dhcpd_wakeup(uint16_t ev, struct pico_socket *s)
|
||||
{
|
||||
uint8_t buf[DHCP_SERVER_MAXMSGSIZE] = {
|
||||
0
|
||||
};
|
||||
int r = 0;
|
||||
|
||||
if (ev != PICO_SOCK_EV_RD)
|
||||
return;
|
||||
|
||||
r = pico_socket_recvfrom(s, buf, DHCP_SERVER_MAXMSGSIZE, NULL, NULL);
|
||||
if (r < 0)
|
||||
return;
|
||||
|
||||
pico_dhcp_server_recv(s, buf, (uint32_t)r);
|
||||
return;
|
||||
}
|
||||
|
||||
int pico_dhcp_server_initiate(struct pico_dhcp_server_setting *setting)
|
||||
{
|
||||
if (!setting || !setting->server_ip.addr) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_dhcp_server_add_setting(setting) == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_dhcp_server_destroy(struct pico_device *dev)
|
||||
{
|
||||
struct pico_dhcp_server_setting *found, test = {
|
||||
0
|
||||
};
|
||||
test.dev = dev;
|
||||
found = pico_tree_findKey(&DHCPSettings, &test);
|
||||
if (!found) {
|
||||
pico_err = PICO_ERR_ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_tree_delete(&DHCPSettings, found);
|
||||
PICO_FREE(found);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* PICO_SUPPORT_DHCP */
|
||||
34
kernel/picotcp/modules/pico_dhcp_server.h
Normal file
34
kernel/picotcp/modules/pico_dhcp_server.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_DHCP_SERVER
|
||||
#define INCLUDE_PICO_DHCP_SERVER
|
||||
#include "pico_defines.h"
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
|
||||
#include "pico_dhcp_common.h"
|
||||
#include "pico_addressing.h"
|
||||
|
||||
struct pico_dhcp_server_setting
|
||||
{
|
||||
uint32_t pool_start;
|
||||
uint32_t pool_next;
|
||||
uint32_t pool_end;
|
||||
uint32_t lease_time;
|
||||
struct pico_device *dev;
|
||||
struct pico_socket *s;
|
||||
struct pico_ip4 server_ip;
|
||||
struct pico_ip4 netmask;
|
||||
uint8_t flags; /* unused atm */
|
||||
};
|
||||
|
||||
/* required field: IP address of the interface to serve, only IPs of this network will be served. */
|
||||
int pico_dhcp_server_initiate(struct pico_dhcp_server_setting *dhcps);
|
||||
|
||||
/* To destroy an existing DHCP server configuration, running on a given interface */
|
||||
int pico_dhcp_server_destroy(struct pico_device *dev);
|
||||
|
||||
#endif /* _INCLUDE_PICO_DHCP_SERVER */
|
||||
#endif
|
||||
846
kernel/picotcp/modules/pico_dns_client.c
Normal file
846
kernel/picotcp/modules/pico_dns_client.c
Normal file
@ -0,0 +1,846 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
.
|
||||
Authors: Kristof Roelants
|
||||
*********************************************************************/
|
||||
#include "pico_config.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_dns_client.h"
|
||||
#include "pico_dns_common.h"
|
||||
#include "pico_tree.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_DNS_CLIENT
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
|
||||
#ifdef DEBUG_DNS
|
||||
#define dns_dbg dbg
|
||||
#else
|
||||
#define dns_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/* DNS response length */
|
||||
#define PICO_DNS_MAX_QUERY_LEN 255
|
||||
#define PICO_DNS_MAX_QUERY_LABEL_LEN 63
|
||||
|
||||
/* DNS client retransmission time (msec) + frequency */
|
||||
#define PICO_DNS_CLIENT_RETRANS 4000
|
||||
#define PICO_DNS_CLIENT_MAX_RETRANS 3
|
||||
|
||||
static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s);
|
||||
static void pico_dns_client_retransmission(pico_time now, void *arg);
|
||||
static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg);
|
||||
|
||||
struct pico_dns_ns
|
||||
{
|
||||
struct pico_ip4 ns; /* nameserver */
|
||||
};
|
||||
|
||||
static int dns_ns_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_dns_ns *a = ka, *b = kb;
|
||||
return pico_ipv4_compare(&a->ns, &b->ns);
|
||||
}
|
||||
static PICO_TREE_DECLARE(NSTable, dns_ns_cmp);
|
||||
|
||||
struct pico_dns_query
|
||||
{
|
||||
char *query;
|
||||
uint16_t len;
|
||||
uint16_t id;
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
uint8_t retrans;
|
||||
struct pico_dns_ns q_ns;
|
||||
struct pico_socket *s;
|
||||
void (*callback)(char *, void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
static int dns_query_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_dns_query *a = ka, *b = kb;
|
||||
if (a->id == b->id)
|
||||
return 0;
|
||||
|
||||
return (a->id < b->id) ? (-1) : (1);
|
||||
}
|
||||
static PICO_TREE_DECLARE(DNSTable, dns_query_cmp);
|
||||
|
||||
static int pico_dns_client_del_ns(struct pico_ip4 *ns_addr)
|
||||
{
|
||||
struct pico_dns_ns test = {{0}}, *found = NULL;
|
||||
|
||||
test.ns = *ns_addr;
|
||||
found = pico_tree_findKey(&NSTable, &test);
|
||||
if (!found)
|
||||
return -1;
|
||||
|
||||
pico_tree_delete(&NSTable, found);
|
||||
PICO_FREE(found);
|
||||
|
||||
/* no NS left, add default NS */
|
||||
if (pico_tree_empty(&NSTable))
|
||||
pico_dns_client_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pico_dns_ns *pico_dns_client_add_ns(struct pico_ip4 *ns_addr)
|
||||
{
|
||||
struct pico_dns_ns *dns = NULL, *found = NULL, test = {{0}};
|
||||
struct pico_ip4 zero = {
|
||||
0
|
||||
}; /* 0.0.0.0 */
|
||||
|
||||
/* Do not add 0.0.0.0 addresses, which some DHCP servers might reply */
|
||||
if (!pico_ipv4_compare(ns_addr, &zero))
|
||||
{
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dns = PICO_ZALLOC(sizeof(struct pico_dns_ns));
|
||||
if (!dns) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dns->ns = *ns_addr;
|
||||
|
||||
found = pico_tree_insert(&NSTable, dns);
|
||||
if (found) { /* nameserver already present or out of memory */
|
||||
PICO_FREE(dns);
|
||||
if ((void *)found == (void *)&LEAF)
|
||||
return NULL;
|
||||
else
|
||||
return found;
|
||||
}
|
||||
|
||||
/* default NS found, remove it */
|
||||
pico_string_to_ipv4(PICO_DNS_NS_DEFAULT, (uint32_t *)&test.ns.addr);
|
||||
found = pico_tree_findKey(&NSTable, &test);
|
||||
if (found && (found->ns.addr != ns_addr->addr))
|
||||
pico_dns_client_del_ns(&found->ns);
|
||||
|
||||
return dns;
|
||||
}
|
||||
|
||||
static struct pico_dns_ns pico_dns_client_next_ns(struct pico_ip4 *ns_addr)
|
||||
{
|
||||
struct pico_dns_ns dns = {{0}}, *nxtdns = NULL;
|
||||
struct pico_tree_node *node = NULL, *nxtnode = NULL;
|
||||
|
||||
dns.ns = *ns_addr;
|
||||
node = pico_tree_findNode(&NSTable, &dns);
|
||||
if (!node)
|
||||
return dns; /* keep using current NS */
|
||||
|
||||
nxtnode = pico_tree_next(node);
|
||||
nxtdns = nxtnode->keyValue;
|
||||
if (!nxtdns)
|
||||
nxtdns = (struct pico_dns_ns *)pico_tree_first(&NSTable);
|
||||
|
||||
return *nxtdns;
|
||||
}
|
||||
|
||||
static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_header *hdr, uint16_t len, struct pico_dns_question_suffix *suffix,
|
||||
void (*callback)(char *, void *), void *arg)
|
||||
{
|
||||
struct pico_dns_query *q = NULL, *found = NULL;
|
||||
|
||||
q = PICO_ZALLOC(sizeof(struct pico_dns_query));
|
||||
if (!q)
|
||||
return NULL;
|
||||
|
||||
q->query = (char *)hdr;
|
||||
q->len = len;
|
||||
q->id = short_be(hdr->id);
|
||||
q->qtype = short_be(suffix->qtype);
|
||||
q->qclass = short_be(suffix->qclass);
|
||||
q->retrans = 1;
|
||||
q->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable));
|
||||
q->callback = callback;
|
||||
q->arg = arg;
|
||||
q->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dns_client_callback);
|
||||
if (!q->s) {
|
||||
PICO_FREE(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
found = pico_tree_insert(&DNSTable, q);
|
||||
if (found) {
|
||||
if ((void *)found != (void *)&LEAF) /* If found == &LEAF we're out of memory and pico_err is set */
|
||||
pico_err = PICO_ERR_EAGAIN;
|
||||
pico_socket_close(q->s);
|
||||
PICO_FREE(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static int pico_dns_client_del_query(uint16_t id)
|
||||
{
|
||||
struct pico_dns_query test = {
|
||||
0
|
||||
}, *found = NULL;
|
||||
|
||||
test.id = id;
|
||||
found = pico_tree_findKey(&DNSTable, &test);
|
||||
if (!found)
|
||||
return -1;
|
||||
|
||||
PICO_FREE(found->query);
|
||||
pico_socket_close(found->s);
|
||||
pico_tree_delete(&DNSTable, found);
|
||||
PICO_FREE(found);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pico_dns_query *pico_dns_client_find_query(uint16_t id)
|
||||
{
|
||||
struct pico_dns_query test = {
|
||||
0
|
||||
}, *found = NULL;
|
||||
|
||||
test.id = id;
|
||||
found = pico_tree_findKey(&DNSTable, &test);
|
||||
if (found)
|
||||
return found;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* seek end of string */
|
||||
static char *pico_dns_client_seek(char *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
while (*ptr != 0)
|
||||
ptr++;
|
||||
return ptr + 1;
|
||||
}
|
||||
|
||||
static struct pico_dns_query *pico_dns_client_idcheck(uint16_t id)
|
||||
{
|
||||
struct pico_dns_query test = {
|
||||
0
|
||||
};
|
||||
|
||||
test.id = id;
|
||||
return pico_tree_findKey(&DNSTable, &test);
|
||||
}
|
||||
|
||||
static int pico_dns_client_query_header(struct pico_dns_header *hdr)
|
||||
{
|
||||
uint16_t id = 0;
|
||||
uint8_t retry = 32;
|
||||
|
||||
do {
|
||||
id = (uint16_t)(pico_rand() & 0xFFFFU);
|
||||
dns_dbg("DNS: generated id %u\n", id);
|
||||
} while (retry-- && pico_dns_client_idcheck(id));
|
||||
if (!retry)
|
||||
return -1;
|
||||
|
||||
hdr->id = short_be(id);
|
||||
pico_dns_fill_packet_header(hdr, 1, 0, 0, 0); /* 1 question, 0 answers */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_check_header(struct pico_dns_header *pre)
|
||||
{
|
||||
if (pre->qr != PICO_DNS_QR_RESPONSE || pre->opcode != PICO_DNS_OPCODE_QUERY || pre->rcode != PICO_DNS_RCODE_NO_ERROR) {
|
||||
dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", pre->opcode, pre->tc, pre->rcode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (short_be(pre->ancount) < 1) {
|
||||
dns_dbg("DNS ERROR: ancount < 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_check_qsuffix(struct pico_dns_question_suffix *suf, struct pico_dns_query *q)
|
||||
{
|
||||
if (!suf)
|
||||
return -1;
|
||||
|
||||
if (short_be(suf->qtype) != q->qtype || short_be(suf->qclass) != q->qclass) {
|
||||
dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->qtype), short_be(suf->qclass));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_check_url(struct pico_dns_header *resp, struct pico_dns_query *q)
|
||||
{
|
||||
char *recv_name = (char*)(resp) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
|
||||
char *exp_name = (char *)(q->query) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
|
||||
if (strcasecmp(recv_name, exp_name) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_check_asuffix(struct pico_dns_record_suffix *suf, struct pico_dns_query *q)
|
||||
{
|
||||
if (!suf) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (short_be(suf->rtype) != q->qtype || short_be(suf->rclass) != q->qclass) {
|
||||
dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->rtype), short_be(suf->rclass));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (long_be(suf->rttl) > PICO_DNS_MAX_TTL) {
|
||||
dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", long_be(suf->rttl), PICO_DNS_MAX_TTL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *pico_dns_client_seek_suffix(char *suf, struct pico_dns_header *pre, struct pico_dns_query *q)
|
||||
{
|
||||
struct pico_dns_record_suffix *asuffix = NULL;
|
||||
uint16_t comp = 0, compression = 0;
|
||||
uint16_t i = 0;
|
||||
|
||||
if (!suf)
|
||||
return NULL;
|
||||
|
||||
while (i++ < short_be(pre->ancount)) {
|
||||
comp = short_from(suf);
|
||||
compression = short_be(comp);
|
||||
switch (compression >> 14)
|
||||
{
|
||||
case PICO_DNS_POINTER:
|
||||
while (compression >> 14 == PICO_DNS_POINTER) {
|
||||
dns_dbg("DNS: pointer\n");
|
||||
suf += sizeof(uint16_t);
|
||||
comp = short_from(suf);
|
||||
compression = short_be(comp);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICO_DNS_LABEL:
|
||||
dns_dbg("DNS: label\n");
|
||||
suf = pico_dns_client_seek(suf);
|
||||
break;
|
||||
|
||||
default:
|
||||
dns_dbg("DNS ERROR: incorrect compression (%u) value\n", compression);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
asuffix = (struct pico_dns_record_suffix *)suf;
|
||||
if (!asuffix)
|
||||
break;
|
||||
|
||||
if (pico_dns_client_check_asuffix(asuffix, q) < 0) {
|
||||
suf += (sizeof(struct pico_dns_record_suffix) + short_be(asuffix->rdlength));
|
||||
continue;
|
||||
}
|
||||
|
||||
return suf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int pico_dns_client_send(struct pico_dns_query *q)
|
||||
{
|
||||
uint16_t *paramID = PICO_ZALLOC(sizeof(uint16_t));
|
||||
if (!paramID) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dns_dbg("DNS: sending query to %08X\n", q->q_ns.ns.addr);
|
||||
if (!q->s)
|
||||
goto failure;
|
||||
|
||||
if (pico_socket_connect(q->s, &q->q_ns.ns, short_be(PICO_DNS_NS_PORT)) < 0)
|
||||
goto failure;
|
||||
|
||||
pico_socket_send(q->s, q->query, q->len);
|
||||
*paramID = q->id;
|
||||
if (!pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, paramID)) {
|
||||
dns_dbg("DNS: Failed to start retransmission timer\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failure:
|
||||
PICO_FREE(paramID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pico_dns_client_retransmission(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_dns_query *q = NULL;
|
||||
struct pico_dns_query dummy;
|
||||
IGNORE_PARAMETER(now);
|
||||
|
||||
if(!arg)
|
||||
return;
|
||||
|
||||
/* search for the dns query and free used space */
|
||||
dummy.id = *(uint16_t *)arg;
|
||||
q = (struct pico_dns_query *)pico_tree_findKey(&DNSTable, &dummy);
|
||||
PICO_FREE(arg);
|
||||
|
||||
/* dns query successful? */
|
||||
if (!q) {
|
||||
return;
|
||||
}
|
||||
|
||||
q->retrans++;
|
||||
if (q->retrans <= PICO_DNS_CLIENT_MAX_RETRANS) {
|
||||
q->q_ns = pico_dns_client_next_ns(&q->q_ns.ns);
|
||||
pico_dns_client_send(q);
|
||||
} else {
|
||||
pico_err = PICO_ERR_EIO;
|
||||
q->callback(NULL, q->arg);
|
||||
pico_dns_client_del_query(q->id);
|
||||
}
|
||||
}
|
||||
|
||||
static int pico_dns_client_check_rdlength(uint16_t qtype, uint16_t rdlength)
|
||||
{
|
||||
switch (qtype)
|
||||
{
|
||||
case PICO_DNS_TYPE_A:
|
||||
if (rdlength != PICO_DNS_RR_A_RDLENGTH)
|
||||
return -1;
|
||||
break;
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
case PICO_DNS_TYPE_AAAA:
|
||||
if (rdlength != PICO_DNS_RR_AAAA_RDLENGTH)
|
||||
return -1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_user_callback(struct pico_dns_record_suffix *asuffix, struct pico_dns_query *q)
|
||||
{
|
||||
uint32_t ip = 0;
|
||||
char *str = NULL;
|
||||
char *rdata = (char *) asuffix + sizeof(struct pico_dns_record_suffix);
|
||||
|
||||
if (pico_dns_client_check_rdlength(q->qtype, short_be(asuffix->rdlength)) < 0) {
|
||||
dns_dbg("DNS ERROR: Invalid RR rdlength: %u\n", short_be(asuffix->rdlength));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (q->qtype)
|
||||
{
|
||||
case PICO_DNS_TYPE_A:
|
||||
ip = long_from(rdata);
|
||||
str = PICO_ZALLOC(PICO_DNS_IPV4_ADDR_LEN);
|
||||
pico_ipv4_to_string(str, ip);
|
||||
break;
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
case PICO_DNS_TYPE_AAAA:
|
||||
{
|
||||
struct pico_ip6 ip6;
|
||||
memcpy(&ip6.addr, rdata, sizeof(struct pico_ip6));
|
||||
str = PICO_ZALLOC(PICO_DNS_IPV6_ADDR_LEN);
|
||||
pico_ipv6_to_string(str, ip6.addr);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case PICO_DNS_TYPE_PTR:
|
||||
/* TODO: check for decompression / rdlength vs. decompressed length */
|
||||
pico_dns_notation_to_name(rdata, short_be(asuffix->rdlength));
|
||||
str = PICO_ZALLOC((size_t)(short_be(asuffix->rdlength) -
|
||||
PICO_DNS_LABEL_INITIAL));
|
||||
if (!str) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(str, rdata + PICO_DNS_LABEL_INITIAL, short_be(asuffix->rdlength) - PICO_DNS_LABEL_INITIAL);
|
||||
break;
|
||||
|
||||
default:
|
||||
dns_dbg("DNS ERROR: incorrect qtype (%u)\n", q->qtype);
|
||||
break;
|
||||
}
|
||||
|
||||
if (q->retrans) {
|
||||
q->callback(str, q->arg);
|
||||
q->retrans = 0;
|
||||
pico_dns_client_del_query(q->id);
|
||||
}
|
||||
|
||||
if (str)
|
||||
PICO_FREE(str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char dns_response[PICO_IP_MRU] = {
|
||||
0
|
||||
};
|
||||
|
||||
static void pico_dns_try_fallback_cname(struct pico_dns_query *q, struct pico_dns_header *h, struct pico_dns_question_suffix *qsuffix)
|
||||
{
|
||||
uint16_t type = q->qtype;
|
||||
uint16_t proto = PICO_PROTO_IPV4;
|
||||
struct pico_dns_record_suffix *asuffix = NULL;
|
||||
char *p_asuffix = NULL;
|
||||
char *cname_orig = NULL;
|
||||
char *cname = NULL;
|
||||
uint16_t cname_len;
|
||||
|
||||
/* Try to use CNAME only if A or AAAA query is ongoing */
|
||||
if (type != PICO_DNS_TYPE_A && type != PICO_DNS_TYPE_AAAA)
|
||||
return;
|
||||
|
||||
if (type == PICO_DNS_TYPE_AAAA)
|
||||
proto = PICO_PROTO_IPV6;
|
||||
|
||||
q->qtype = PICO_DNS_TYPE_CNAME;
|
||||
p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
|
||||
p_asuffix = pico_dns_client_seek_suffix(p_asuffix, h, q);
|
||||
if (!p_asuffix) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Found CNAME response. Re-initiating query. */
|
||||
asuffix = (struct pico_dns_record_suffix *)p_asuffix;
|
||||
cname = pico_dns_decompress_name((char *)asuffix + sizeof(struct pico_dns_record_suffix), (pico_dns_packet *)h); /* allocates memory! */
|
||||
cname_orig = cname; /* to free later */
|
||||
|
||||
if (cname == NULL)
|
||||
return;
|
||||
|
||||
cname_len = (uint16_t)(pico_dns_strlen(cname) + 1);
|
||||
|
||||
pico_dns_notation_to_name(cname, cname_len);
|
||||
if (cname[0] == '.')
|
||||
cname++;
|
||||
|
||||
dns_dbg("Restarting query for name '%s'\n", cname);
|
||||
pico_dns_client_getaddr_init(cname, proto, q->callback, q->arg);
|
||||
PICO_FREE(cname_orig);
|
||||
pico_dns_client_del_query(q->id);
|
||||
}
|
||||
|
||||
static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)
|
||||
{
|
||||
struct pico_dns_header *header = NULL;
|
||||
char *domain;
|
||||
struct pico_dns_question_suffix *qsuffix = NULL;
|
||||
struct pico_dns_record_suffix *asuffix = NULL;
|
||||
struct pico_dns_query *q = NULL;
|
||||
char *p_asuffix = NULL;
|
||||
|
||||
if (ev == PICO_SOCK_EV_ERR) {
|
||||
dns_dbg("DNS: socket error received\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev & PICO_SOCK_EV_RD) {
|
||||
if (pico_socket_read(s, dns_response, PICO_IP_MRU) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
header = (struct pico_dns_header *)dns_response;
|
||||
domain = (char *)header + sizeof(struct pico_dns_header);
|
||||
qsuffix = (struct pico_dns_question_suffix *)pico_dns_client_seek(domain);
|
||||
/* valid asuffix is determined dynamically later on */
|
||||
|
||||
if (pico_dns_client_check_header(header) < 0)
|
||||
return;
|
||||
|
||||
q = pico_dns_client_find_query(short_be(header->id));
|
||||
if (!q)
|
||||
return;
|
||||
|
||||
if (pico_dns_client_check_qsuffix(qsuffix, q) < 0)
|
||||
return;
|
||||
|
||||
if (pico_dns_client_check_url(header, q) < 0)
|
||||
return;
|
||||
|
||||
p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
|
||||
p_asuffix = pico_dns_client_seek_suffix(p_asuffix, header, q);
|
||||
if (!p_asuffix) {
|
||||
pico_dns_try_fallback_cname(q, header, qsuffix);
|
||||
return;
|
||||
}
|
||||
|
||||
asuffix = (struct pico_dns_record_suffix *)p_asuffix;
|
||||
pico_dns_client_user_callback(asuffix, q);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int pico_dns_create_message(struct pico_dns_header **header, struct pico_dns_question_suffix **qsuffix, enum pico_dns_arpa arpa, const char *url, uint16_t *urlen, uint16_t *hdrlen)
|
||||
{
|
||||
char *domain;
|
||||
char inaddr_arpa[14];
|
||||
uint16_t strlen = 0, arpalen = 0;
|
||||
|
||||
if (!url) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(arpa == PICO_DNS_ARPA4) {
|
||||
strcpy(inaddr_arpa, ".in-addr.arpa");
|
||||
strlen = pico_dns_strlen(url);
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
else if (arpa == PICO_DNS_ARPA6) {
|
||||
strcpy(inaddr_arpa, ".IP6.ARPA");
|
||||
strlen = STRLEN_PTR_IP6;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
strcpy(inaddr_arpa, "");
|
||||
strlen = pico_dns_strlen(url);
|
||||
}
|
||||
|
||||
arpalen = pico_dns_strlen(inaddr_arpa);
|
||||
*urlen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + arpalen + PICO_DNS_LABEL_ROOT);
|
||||
*hdrlen = (uint16_t)(sizeof(struct pico_dns_header) + *urlen + sizeof(struct pico_dns_question_suffix));
|
||||
*header = PICO_ZALLOC(*hdrlen);
|
||||
if (!*header) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*header = (struct pico_dns_header *)*header;
|
||||
domain = (char *) *header + sizeof(struct pico_dns_header);
|
||||
*qsuffix = (struct pico_dns_question_suffix *)(domain + *urlen);
|
||||
|
||||
if(arpa == PICO_DNS_ARPA4) {
|
||||
memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
|
||||
pico_dns_mirror_addr(domain + PICO_DNS_LABEL_INITIAL);
|
||||
memcpy(domain + PICO_DNS_LABEL_INITIAL + strlen, inaddr_arpa, arpalen);
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
else if (arpa == PICO_DNS_ARPA6) {
|
||||
pico_dns_ipv6_set_ptr(url, domain + PICO_DNS_LABEL_INITIAL);
|
||||
memcpy(domain + PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6, inaddr_arpa, arpalen);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
|
||||
}
|
||||
|
||||
/* assemble dns message */
|
||||
pico_dns_client_query_header(*header);
|
||||
pico_dns_name_to_dns_notation(domain, strlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_addr_label_check_len(const char *url)
|
||||
{
|
||||
const char *p, *label;
|
||||
int count;
|
||||
label = url;
|
||||
p = label;
|
||||
|
||||
while(*p != (char) 0) {
|
||||
count = 0;
|
||||
while((*p != (char)0)) {
|
||||
if (*p == '.') {
|
||||
label = ++p;
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
p++;
|
||||
if (count > PICO_DNS_MAX_QUERY_LABEL_LEN)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_getaddr_check(const char *url, void (*callback)(char *, void *))
|
||||
{
|
||||
if (!url || !callback) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlen(url) > PICO_DNS_MAX_QUERY_LEN) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_dns_client_addr_label_check_len(url) < 0) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg)
|
||||
{
|
||||
struct pico_dns_header *header = NULL;
|
||||
struct pico_dns_question_suffix *qsuffix = NULL;
|
||||
struct pico_dns_query *q = NULL;
|
||||
uint16_t len = 0, lblen = 0;
|
||||
(void)proto;
|
||||
|
||||
if (pico_dns_client_getaddr_check(url, callback) < 0)
|
||||
return -1;
|
||||
|
||||
if(pico_dns_create_message(&header, &qsuffix, PICO_DNS_NO_ARPA, url, &lblen, &len) != 0)
|
||||
return -1;
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
if (proto == PICO_PROTO_IPV6) {
|
||||
pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
|
||||
} else
|
||||
#endif
|
||||
pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);
|
||||
|
||||
q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
|
||||
if (!q) {
|
||||
PICO_FREE(header);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_dns_client_send(q) < 0) {
|
||||
pico_dns_client_del_query(q->id); /* frees msg */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_dns_client_getaddr(const char *url, void (*callback)(char *, void *), void *arg)
|
||||
{
|
||||
return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV4, callback, arg);
|
||||
}
|
||||
|
||||
int pico_dns_client_getaddr6(const char *url, void (*callback)(char *, void *), void *arg)
|
||||
{
|
||||
return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV6, callback, arg);
|
||||
}
|
||||
|
||||
static int pico_dns_getname_univ(const char *ip, void (*callback)(char *, void *), void *arg, enum pico_dns_arpa arpa)
|
||||
{
|
||||
struct pico_dns_header *header = NULL;
|
||||
struct pico_dns_question_suffix *qsuffix = NULL;
|
||||
struct pico_dns_query *q = NULL;
|
||||
uint16_t len = 0, lblen = 0;
|
||||
|
||||
if (!ip || !callback) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(pico_dns_create_message(&header, &qsuffix, arpa, ip, &lblen, &len) != 0)
|
||||
return -1;
|
||||
|
||||
pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
|
||||
q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
|
||||
if (!q) {
|
||||
PICO_FREE(header);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_dns_client_send(q) < 0) {
|
||||
pico_dns_client_del_query(q->id); /* frees header */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
|
||||
{
|
||||
return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA4);
|
||||
}
|
||||
|
||||
|
||||
int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg)
|
||||
{
|
||||
return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA6);
|
||||
}
|
||||
|
||||
int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag)
|
||||
{
|
||||
if (!ns) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case PICO_DNS_NS_ADD:
|
||||
if (!pico_dns_client_add_ns(ns))
|
||||
return -1;
|
||||
|
||||
break;
|
||||
|
||||
case PICO_DNS_NS_DEL:
|
||||
if (pico_dns_client_del_ns(ns) < 0) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_dns_client_init(void)
|
||||
{
|
||||
struct pico_ip4 default_ns = {
|
||||
0
|
||||
};
|
||||
|
||||
if (pico_string_to_ipv4(PICO_DNS_NS_DEFAULT, (uint32_t *)&default_ns.addr) < 0)
|
||||
return -1;
|
||||
|
||||
return pico_dns_client_nameserver(&default_ns, PICO_DNS_NS_ADD);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int pico_dns_client_init(void)
|
||||
{
|
||||
dbg("ERROR Trying to initialize DNS module: IPv4 not supported in this build.\n");
|
||||
return -1;
|
||||
}
|
||||
#endif /* PICO_SUPPORT_IPV4 */
|
||||
|
||||
|
||||
#endif /* PICO_SUPPORT_DNS_CLIENT */
|
||||
|
||||
50
kernel/picotcp/modules/pico_dns_client.h
Normal file
50
kernel/picotcp/modules/pico_dns_client.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Kristof Roelants
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_DNS_CLIENT
|
||||
#define INCLUDE_PICO_DNS_CLIENT
|
||||
|
||||
#define PICO_DNS_NS_DEL 0
|
||||
#define PICO_DNS_NS_ADD 1
|
||||
#include "pico_config.h"
|
||||
|
||||
/* Compression values */
|
||||
#define PICO_DNS_LABEL 0
|
||||
#define PICO_DNS_POINTER 3
|
||||
|
||||
/* Label len */
|
||||
#define PICO_DNS_LABEL_INITIAL 1u
|
||||
#define PICO_DNS_LABEL_ROOT 1
|
||||
|
||||
/* TTL values */
|
||||
#define PICO_DNS_MAX_TTL 604800 /* one week */
|
||||
|
||||
/* Len of an IPv4 address string */
|
||||
#define PICO_DNS_IPV4_ADDR_LEN 16
|
||||
#define PICO_DNS_IPV6_ADDR_LEN 54
|
||||
|
||||
/* Default nameservers + port */
|
||||
#define PICO_DNS_NS_DEFAULT "208.67.222.222"
|
||||
#define PICO_DNS_NS_PORT 53
|
||||
|
||||
/* RDLENGTH for A and AAAA RR's */
|
||||
#define PICO_DNS_RR_A_RDLENGTH 4
|
||||
#define PICO_DNS_RR_AAAA_RDLENGTH 16
|
||||
|
||||
int pico_dns_client_init(void);
|
||||
/* flag is PICO_DNS_NS_DEL or PICO_DNS_NS_ADD */
|
||||
int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag);
|
||||
int pico_dns_client_getaddr(const char *url, void (*callback)(char *ip, void *arg), void *arg);
|
||||
int pico_dns_client_getname(const char *ip, void (*callback)(char *url, void *arg), void *arg);
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
int pico_dns_client_getaddr6(const char *url, void (*callback)(char *, void *), void *arg);
|
||||
int pico_dns_client_getname6(const char *url, void (*callback)(char *, void *), void *arg);
|
||||
#endif
|
||||
|
||||
#endif /* _INCLUDE_PICO_DNS_CLIENT */
|
||||
1784
kernel/picotcp/modules/pico_dns_common.c
Normal file
1784
kernel/picotcp/modules/pico_dns_common.c
Normal file
File diff suppressed because it is too large
Load Diff
528
kernel/picotcp/modules/pico_dns_common.h
Normal file
528
kernel/picotcp/modules/pico_dns_common.h
Normal file
@ -0,0 +1,528 @@
|
||||
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
.
|
||||
Authors: Toon Stegen, Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_DNS_COMMON
|
||||
#define INCLUDE_PICO_DNS_COMMON
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_tree.h"
|
||||
|
||||
/* TYPE values */
|
||||
#define PICO_DNS_TYPE_A 1
|
||||
#define PICO_DNS_TYPE_CNAME 5
|
||||
#define PICO_DNS_TYPE_PTR 12
|
||||
#define PICO_DNS_TYPE_TXT 16
|
||||
#define PICO_DNS_TYPE_AAAA 28
|
||||
#define PICO_DNS_TYPE_SRV 33
|
||||
#define PICO_DNS_TYPE_NSEC 47
|
||||
#define PICO_DNS_TYPE_ANY 255
|
||||
|
||||
/* CLASS values */
|
||||
#define PICO_DNS_CLASS_IN 1
|
||||
|
||||
/* FLAG values */
|
||||
#define PICO_DNS_QR_QUERY 0
|
||||
#define PICO_DNS_QR_RESPONSE 1
|
||||
#define PICO_DNS_OPCODE_QUERY 0
|
||||
#define PICO_DNS_OPCODE_IQUERY 1
|
||||
#define PICO_DNS_OPCODE_STATUS 2
|
||||
#define PICO_DNS_AA_NO_AUTHORITY 0
|
||||
#define PICO_DNS_AA_IS_AUTHORITY 1
|
||||
#define PICO_DNS_TC_NO_TRUNCATION 0
|
||||
#define PICO_DNS_TC_IS_TRUNCATED 1
|
||||
#define PICO_DNS_RD_NO_DESIRE 0
|
||||
#define PICO_DNS_RD_IS_DESIRED 1
|
||||
#define PICO_DNS_RA_NO_SUPPORT 0
|
||||
#define PICO_DNS_RA_IS_SUPPORTED 1
|
||||
#define PICO_DNS_RCODE_NO_ERROR 0
|
||||
#define PICO_DNS_RCODE_EFORMAT 1
|
||||
#define PICO_DNS_RCODE_ESERVER 2
|
||||
#define PICO_DNS_RCODE_ENAME 3
|
||||
#define PICO_DNS_RCODE_ENOIMP 4
|
||||
#define PICO_DNS_RCODE_EREFUSED 5
|
||||
|
||||
#define PICO_ARPA_IPV4_SUFFIX ".in-addr.arpa"
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
#define STRLEN_PTR_IP6 63
|
||||
#define PICO_ARPA_IPV6_SUFFIX ".IP6.ARPA"
|
||||
#endif
|
||||
|
||||
/* Used in pico_dns_rdata_cmp */
|
||||
#define PICO_DNS_CASE_SENSITIVE 0x00u
|
||||
#define PICO_DNS_CASE_INSENSITIVE 0x01u
|
||||
|
||||
#define PICO_DNS_NAMEBUF_SIZE (256)
|
||||
|
||||
enum pico_dns_arpa
|
||||
{
|
||||
PICO_DNS_ARPA4,
|
||||
PICO_DNS_ARPA6,
|
||||
PICO_DNS_NO_ARPA,
|
||||
};
|
||||
|
||||
/* flags split in 2x uint8 due to endianness */
|
||||
PACKED_STRUCT_DEF pico_dns_header
|
||||
{
|
||||
uint16_t id; /* Packet id */
|
||||
uint8_t rd : 1; /* Recursion Desired */
|
||||
uint8_t tc : 1; /* TrunCation */
|
||||
uint8_t aa : 1; /* Authoritative Answer */
|
||||
uint8_t opcode : 4; /* Opcode */
|
||||
uint8_t qr : 1; /* Query/Response */
|
||||
uint8_t rcode : 4; /* Response code */
|
||||
uint8_t z : 3; /* Zero */
|
||||
uint8_t ra : 1; /* Recursion Available */
|
||||
uint16_t qdcount; /* Question count */
|
||||
uint16_t ancount; /* Answer count */
|
||||
uint16_t nscount; /* Authority count */
|
||||
uint16_t arcount; /* Additional count */
|
||||
};
|
||||
typedef struct pico_dns_header pico_dns_packet;
|
||||
|
||||
/* Question fixed-sized fields */
|
||||
PACKED_STRUCT_DEF pico_dns_question_suffix
|
||||
{
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
};
|
||||
|
||||
/* Resource record fixed-sized fields */
|
||||
PACKED_STRUCT_DEF pico_dns_record_suffix
|
||||
{
|
||||
uint16_t rtype;
|
||||
uint16_t rclass;
|
||||
uint32_t rttl;
|
||||
uint16_t rdlength;
|
||||
};
|
||||
|
||||
/* DNS QUESTION */
|
||||
struct pico_dns_question
|
||||
{
|
||||
char *qname;
|
||||
struct pico_dns_question_suffix *qsuffix;
|
||||
uint16_t qname_length;
|
||||
uint8_t proto;
|
||||
};
|
||||
|
||||
/* DNS RECORD */
|
||||
struct pico_dns_record
|
||||
{
|
||||
char *rname;
|
||||
struct pico_dns_record_suffix *rsuffix;
|
||||
uint8_t *rdata;
|
||||
uint16_t rname_length;
|
||||
};
|
||||
|
||||
/* MARK: v NAME & IP FUNCTIONS */
|
||||
|
||||
/* ****************************************************************************
|
||||
* Checks if the DNS name doesn't exceed 256 bytes including zero-byte.
|
||||
*
|
||||
* @param namelen Length of the DNS name-string including zero-byte
|
||||
* @return 0 when the length is correct
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_check_namelen( uint16_t namelen );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Returns the length of a name in a DNS-packet as if DNS name compression
|
||||
* would be applied to the packet. If there's no compression present this
|
||||
* returns the strlen. If there's compression present this returns the length
|
||||
* until the compression-pointer + 1.
|
||||
*
|
||||
* @param name Compressed name you want the calculate the strlen from
|
||||
* @return Returns strlen of a compressed name, takes the first byte of compr-
|
||||
* ession pointer into account but not the second byte, which acts
|
||||
* like a trailing zero-byte.
|
||||
* ****************************************************************************/
|
||||
uint16_t
|
||||
pico_dns_namelen_comp( char *name );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Returns the uncompressed name in DNS name format when DNS name compression
|
||||
* is applied to the packet-buffer.
|
||||
*
|
||||
* @param name Compressed name, should be in the bounds of the actual packet
|
||||
* @param packet Packet that contains the compressed name
|
||||
* @return Returns the decompressed name, NULL on failure.
|
||||
* ****************************************************************************/
|
||||
char *
|
||||
pico_dns_decompress_name( char *name, pico_dns_packet *packet );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Converts a DNS name in DNS name format to a name in URL format. Provides
|
||||
* space for the name in URL format as well. PICO_FREE() should be called on
|
||||
* the returned string buffer that contains the name in URL format.
|
||||
*
|
||||
* @param qname DNS name in DNS name format to convert
|
||||
* @return Returns a pointer to a string-buffer with the URL name on success.
|
||||
* ****************************************************************************/
|
||||
char *
|
||||
pico_dns_qname_to_url( const char *qname );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Converts a DNS name in URL format to name in DNS name format. Provides
|
||||
* space for the DNS name as well. PICO_FREE() should be called on the returned
|
||||
* string buffer that contains the DNS name.
|
||||
*
|
||||
* @param url DNS name in URL format to convert
|
||||
* @return Returns a pointer to a string-buffer with the DNS name on success.
|
||||
* ****************************************************************************/
|
||||
char *
|
||||
pico_dns_url_to_qname( const char *url );
|
||||
|
||||
/* ****************************************************************************
|
||||
* @param url String-buffer
|
||||
* @return Length of string-buffer in an uint16_t
|
||||
* ****************************************************************************/
|
||||
uint16_t
|
||||
pico_dns_strlen( const char *url );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Replaces .'s in a DNS name in URL format by the label lengths. So it
|
||||
* actually converts a name in URL format to a name in DNS name format.
|
||||
* f.e. "*www.google.be" => "3www6google2be0"
|
||||
*
|
||||
* @param url Location to buffer with name in URL format. The URL needs to
|
||||
* be +1 byte offset in the actual buffer. Size is should be
|
||||
* strlen(url) + 2.
|
||||
* @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow
|
||||
* @return 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int pico_dns_name_to_dns_notation( char *url, uint16_t maxlen );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Replaces the label lengths in a DNS-name by .'s. So it actually converts a
|
||||
* name in DNS format to a name in URL format.
|
||||
* f.e. 3www6google2be0 => .www.google.be
|
||||
*
|
||||
* @param ptr Location to buffer with name in DNS name format
|
||||
* @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow
|
||||
* @return 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int pico_dns_notation_to_name( char *ptr, uint16_t maxlen );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Determines the length of the first label of a DNS name in URL-format
|
||||
*
|
||||
* @param url DNS name in URL-format
|
||||
* @return Length of the first label of DNS name in URL-format
|
||||
* ****************************************************************************/
|
||||
uint16_t
|
||||
pico_dns_first_label_length( const char *url );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Mirrors a dotted IPv4-address string.
|
||||
* f.e. 192.168.0.1 => 1.0.168.192
|
||||
*
|
||||
* @param ptr
|
||||
* @return 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_mirror_addr( char *ptr );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Convert an IPv6-address in string-format to a IPv6-address in nibble-format.
|
||||
* Doesn't add a IPv6 ARPA-suffix though.
|
||||
*
|
||||
* @param ip IPv6-address stored as a string
|
||||
* @param dst Destination to store IPv6-address in nibble-format
|
||||
* ****************************************************************************/
|
||||
void
|
||||
pico_dns_ipv6_set_ptr( const char *ip, char *dst );
|
||||
|
||||
/* MARK: QUESTION FUNCTIONS */
|
||||
|
||||
/* ****************************************************************************
|
||||
* Deletes a single DNS Question.
|
||||
*
|
||||
* @param question Void-pointer to DNS Question. Can be used with pico_tree_-
|
||||
* destroy.
|
||||
* @return Returns 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_question_delete( void **question);
|
||||
|
||||
/* ****************************************************************************
|
||||
* Fills in the DNS question suffix-fields with the correct values.
|
||||
*
|
||||
* todo: Update pico_dns_client to make the same mechanism possible as with
|
||||
* filling DNS Resource Record-suffixes. This function shouldn't be an
|
||||
* API-function.
|
||||
*
|
||||
* @param suf Pointer to the suffix member of the DNS question.
|
||||
* @param qtype DNS type of the DNS question to be.
|
||||
* @param qclass DNS class of the DNS question to be.
|
||||
* @return Returns 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_question_fill_suffix( struct pico_dns_question_suffix *suf,
|
||||
uint16_t qtype,
|
||||
uint16_t qclass );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Creates a standalone DNS Question with a given name and type.
|
||||
*
|
||||
* @param url DNS question name in URL format. Will be converted to DNS
|
||||
* name notation format.
|
||||
* @param len Will be filled with the total length of the DNS question.
|
||||
* @param proto Protocol for which you want to create a question. Can be
|
||||
* either PICO_PROTO_IPV4 or PICO_PROTO_IPV6.
|
||||
* @param qtype DNS type of the question to be.
|
||||
* @param qclass DNS class of the question to be.
|
||||
* @param reverse When this is true, a reverse resolution name will be gene-
|
||||
* from the URL
|
||||
* @return Returns pointer to the created DNS Question on success, NULL on
|
||||
* failure.
|
||||
* ****************************************************************************/
|
||||
struct pico_dns_question *
|
||||
pico_dns_question_create( const char *url,
|
||||
uint16_t *len,
|
||||
uint8_t proto,
|
||||
uint16_t qtype,
|
||||
uint16_t qclass,
|
||||
uint8_t reverse );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Decompresses the name of a single DNS question.
|
||||
*
|
||||
* @param question Question you want to decompress the name of
|
||||
* @param packet Packet in which the DNS question is contained.
|
||||
* @return Pointer to original name of the DNS question before decompressing.
|
||||
* ****************************************************************************/
|
||||
char *
|
||||
pico_dns_question_decompress( struct pico_dns_question *question,
|
||||
pico_dns_packet *packet );
|
||||
|
||||
/* MARK: RESOURCE RECORD FUNCTIONS */
|
||||
|
||||
/* ****************************************************************************
|
||||
* Deletes a single DNS resource record.
|
||||
*
|
||||
* @param record Void-pointer to DNS record. Can be used with pico_tree_destroy
|
||||
* @return Returns 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_record_delete( void **record );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Just makes a hardcopy from a single DNS Resource Record
|
||||
*
|
||||
* @param record DNS record you want to copy
|
||||
* @return Pointer to copy of DNS record.
|
||||
* ****************************************************************************/
|
||||
struct pico_dns_record *
|
||||
pico_dns_record_copy( struct pico_dns_record *record );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Create a standalone DNS Resource Record with given name, type and data.
|
||||
*
|
||||
* @param url DNS rrecord name in URL format. Will be converted to DNS
|
||||
* name notation format.
|
||||
* @param _rdata Memory buffer with data to insert in the resource record. If
|
||||
* data of record should contain a DNS name, the name in the
|
||||
* databuffer needs to be in URL-format.
|
||||
* @param datalen The exact length in bytes of the _rdata-buffer. If data of
|
||||
* record should contain a DNS name, datalen needs to be
|
||||
* pico_dns_strlen(_rdata).
|
||||
* @param len Will be filled with the total length of the DNS rrecord.
|
||||
* @param rtype DNS type of the resource record to be.
|
||||
* @param rclass DNS class of the resource record to be.
|
||||
* @param rttl DNS ttl of the resource record to be.
|
||||
* @return Returns pointer to the created DNS Resource Record
|
||||
* ****************************************************************************/
|
||||
struct pico_dns_record *
|
||||
pico_dns_record_create( const char *url,
|
||||
void *_rdata,
|
||||
uint16_t datalen,
|
||||
uint16_t *len,
|
||||
uint16_t rtype,
|
||||
uint16_t rclass,
|
||||
uint32_t rttl );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Decompresses the name of single DNS record.
|
||||
*
|
||||
* @param record DNS record to decompress the name of.
|
||||
* @param packet Packet in which is DNS record is present
|
||||
* @return Pointer to original name of the DNS record before decompressing.
|
||||
* ****************************************************************************/
|
||||
char *
|
||||
pico_dns_record_decompress( struct pico_dns_record *record,
|
||||
pico_dns_packet *packet );
|
||||
|
||||
/* MARK: COMPARING */
|
||||
|
||||
/* ****************************************************************************
|
||||
* Compares two databuffers against each other.
|
||||
*
|
||||
* @param a 1st Memory buffer to compare
|
||||
* @param b 2nd Memory buffer to compare
|
||||
* @param rdlength_a Length of 1st memory buffer
|
||||
* @param rdlength_b Length of 2nd memory buffer
|
||||
* @param caseinsensitive Whether or not the bytes are compared
|
||||
* case-insensitive. Should be either
|
||||
* PICO_DNS_CASE_SENSITIVE or PICO_DNS_CASE_INSENSITIVE
|
||||
* @return 0 when the buffers are equal, returns difference when they're not.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_rdata_cmp( uint8_t *a, uint8_t *b,
|
||||
uint16_t rdlength_a, uint16_t rdlength_b, uint8_t caseinsensitive );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Compares 2 DNS questions
|
||||
*
|
||||
* @param qa DNS question A as a void-pointer (for pico_tree)
|
||||
* @param qb DNS question A as a void-pointer (for pico_tree)
|
||||
* @return 0 when questions are equal, returns difference when they're not.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_question_cmp( void *qa,
|
||||
void *qb );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Compares 2 DNS records by type and name only
|
||||
*
|
||||
* @param ra DNS record A as a void-pointer (for pico_tree)
|
||||
* @param rb DNS record B as a void-pointer (for pico_tree)
|
||||
* @return 0 when name and type of records are equal, returns difference when
|
||||
* they're not.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_record_cmp_name_type( void *ra,
|
||||
void *rb );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Compares 2 DNS records by type, name AND rdata for a truly unique result
|
||||
*
|
||||
* @param ra DNS record A as a void-pointer (for pico_tree)
|
||||
* @param rb DNS record B as a void-pointer (for pico_tree)
|
||||
* @return 0 when records are equal, returns difference when they're not
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_record_cmp( void *ra,
|
||||
void *rb );
|
||||
|
||||
/* MARK: PICO_TREE */
|
||||
|
||||
/* ****************************************************************************
|
||||
* Erases a pico_tree entirely.
|
||||
*
|
||||
* @param tree Pointer to a pico_tree-instance
|
||||
* @param node_delete Helper-function for type-specific deleting.
|
||||
* @return Returns 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_tree_destroy( struct pico_tree *tree, int (*node_delete)(void **));
|
||||
|
||||
/* ****************************************************************************
|
||||
* Determines the amount of nodes in a pico_tree
|
||||
*
|
||||
* @param tree Pointer to pico_tree-instance
|
||||
* @return Amount of items in the tree.
|
||||
* ****************************************************************************/
|
||||
uint16_t
|
||||
pico_tree_count( struct pico_tree *tree );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Definition of DNS question tree
|
||||
* ****************************************************************************/
|
||||
typedef struct pico_tree pico_dns_qtree;
|
||||
#define PICO_DNS_QTREE_DECLARE(name) \
|
||||
pico_dns_qtree (name) = {&LEAF, pico_dns_question_cmp}
|
||||
#define PICO_DNS_QTREE_DESTROY(qtree) \
|
||||
pico_tree_destroy(qtree, pico_dns_question_delete)
|
||||
|
||||
/* ****************************************************************************
|
||||
* Deletes all the questions with given DNS name from a pico_tree
|
||||
*
|
||||
* @param qtree Pointer to pico_tree-instance which contains DNS questions
|
||||
* @param name Name of the questions you want to delete
|
||||
* @return Returns 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_qtree_del_name( struct pico_tree *qtree,
|
||||
const char *name );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Checks whether a question with given name is in the tree or not.
|
||||
*
|
||||
* @param qtree Pointer to pico_tree-instance which contains DNS questions
|
||||
* @param name Name you want to check for
|
||||
* @return 1 when the name is present in the qtree, 0 when it's not.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_qtree_find_name( struct pico_tree *qtree,
|
||||
const char *name );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Definition of DNS record tree
|
||||
* ****************************************************************************/
|
||||
typedef struct pico_tree pico_dns_rtree;
|
||||
#define PICO_DNS_RTREE_DECLARE(name) \
|
||||
pico_dns_rtree (name) = {&LEAF, pico_dns_record_cmp}
|
||||
#define PICO_DNS_RTREE_DESTROY(rtree) \
|
||||
pico_tree_destroy((rtree), pico_dns_record_delete)
|
||||
|
||||
/* MARK: DNS PACKET FUNCTIONS */
|
||||
|
||||
/* ****************************************************************************
|
||||
* Fills the header section of a DNS packet with the correct flags and section
|
||||
* -counts.
|
||||
*
|
||||
* @param hdr Header to fill in.
|
||||
* @param qdcount Amount of questions added to the packet
|
||||
* @param ancount Amount of answer records added to the packet
|
||||
* @param nscount Amount of authority records added to the packet
|
||||
* @param arcount Amount of additional records added to the packet
|
||||
* ****************************************************************************/
|
||||
void
|
||||
pico_dns_fill_packet_header( struct pico_dns_header *hdr,
|
||||
uint16_t qdcount,
|
||||
uint16_t ancount,
|
||||
uint16_t authcount,
|
||||
uint16_t addcount );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Creates a DNS Query packet with given question and resource records to put
|
||||
* the Resource Record Sections. If a NULL-pointer is provided for a certain
|
||||
* tree, no records will be added to that particular section of the packet.
|
||||
*
|
||||
* @param qtree DNS Questions to put in the Question Section
|
||||
* @param antree DNS Records to put in the Answer Section
|
||||
* @param nstree DNS Records to put in the Authority Section
|
||||
* @param artree DNS Records to put in the Additional Section
|
||||
* @param len Will get filled with the entire size of the packet
|
||||
* @return Pointer to created DNS packet
|
||||
* ****************************************************************************/
|
||||
pico_dns_packet *
|
||||
pico_dns_query_create( struct pico_tree *qtree,
|
||||
struct pico_tree *antree,
|
||||
struct pico_tree *nstree,
|
||||
struct pico_tree *artree,
|
||||
uint16_t *len );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Creates a DNS Answer packet with given resource records to put in the
|
||||
* Resource Record Sections. If a NULL-pointer is provided for a certain tree,
|
||||
* no records will be added to that particular section of the packet.
|
||||
*
|
||||
* @param antree DNS Records to put in the Answer Section
|
||||
* @param nstree DNS Records to put in the Authority Section
|
||||
* @param artree DNS Records to put in the Additional Section
|
||||
* @param len Will get filled with the entire size of the packet
|
||||
* @return Pointer to created DNS packet.
|
||||
* ****************************************************************************/
|
||||
pico_dns_packet *
|
||||
pico_dns_answer_create( struct pico_tree *antree,
|
||||
struct pico_tree *nstree,
|
||||
struct pico_tree *artree,
|
||||
uint16_t *len );
|
||||
|
||||
#endif /* _INCLUDE_PICO_DNS_COMMON */
|
||||
568
kernel/picotcp/modules/pico_dns_sd.c
Normal file
568
kernel/picotcp/modules/pico_dns_sd.c
Normal file
@ -0,0 +1,568 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
.
|
||||
Author: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_dns_sd.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_DNS_SD
|
||||
|
||||
/* --- Debugging --- */
|
||||
#ifdef DEBUG_DNS_SD
|
||||
#define dns_sd_dbg dbg
|
||||
#else
|
||||
#define dns_sd_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/* --- PROTOTYPES --- */
|
||||
key_value_pair_t *
|
||||
pico_dns_sd_kv_vector_get( kv_vector *vector, uint16_t index );
|
||||
int
|
||||
pico_dns_sd_kv_vector_erase( kv_vector *vector );
|
||||
/* ------------------- */
|
||||
|
||||
typedef PACKED_STRUCT_DEF pico_dns_srv_record_prefix
|
||||
{
|
||||
uint16_t priority;
|
||||
uint16_t weight;
|
||||
uint16_t port;
|
||||
} pico_dns_srv_record;
|
||||
|
||||
/* ****************************************************************************
|
||||
* Determines the length of the resulting string when a string would be
|
||||
* created from a key-value pair vector.
|
||||
*
|
||||
* @param vector Key-Value pair vector to determine the length of.
|
||||
* @return The length of the key-value pair vector in bytes as if it would be
|
||||
* converted to a string.
|
||||
* ****************************************************************************/
|
||||
static uint16_t
|
||||
pico_dns_sd_kv_vector_strlen( kv_vector *vector )
|
||||
{
|
||||
key_value_pair_t *iterator = NULL;
|
||||
uint16_t i = 0, len = 0;
|
||||
|
||||
/* Check params */
|
||||
if (!vector) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Iterate over the key-value pairs */
|
||||
for (i = 0; i < vector->count; i++) {
|
||||
iterator = pico_dns_sd_kv_vector_get(vector, i);
|
||||
len = (uint16_t) (len + 1u + /* Length byte */
|
||||
strlen(iterator->key) /* Length of the key */);
|
||||
if (iterator->value) {
|
||||
len = (uint16_t) (len + 1u /* '=' char */ +
|
||||
strlen(iterator->value) /* Length of value */);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Creates an mDNS record with the SRV record format.
|
||||
*
|
||||
* @param url Name of the SRV record in URL format.
|
||||
* @param priority Priority, should be 0.
|
||||
* @param weight Weight, should be 0.
|
||||
* @param port Port to register the service on.
|
||||
* @param target_url Hostname of the service-target, in URL-format
|
||||
* @param ttl TTL of the SRV Record
|
||||
* @param flags mDNS record flags to set specifications of the record.
|
||||
* @return Pointer to newly created record on success, NULL on failure.
|
||||
* ****************************************************************************/
|
||||
static struct pico_mdns_record *
|
||||
pico_dns_sd_srv_record_create( const char *url,
|
||||
uint16_t priority,
|
||||
uint16_t weight,
|
||||
uint16_t port,
|
||||
const char *target_url,
|
||||
uint32_t ttl,
|
||||
uint8_t flags )
|
||||
{
|
||||
struct pico_mdns_record *record = NULL;
|
||||
pico_dns_srv_record *srv_data = NULL;
|
||||
char *target_rname = NULL;
|
||||
uint16_t srv_length = 0;
|
||||
|
||||
/* Check params */
|
||||
if (!url || !target_url) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Determine the length the rdata buf needs to be */
|
||||
srv_length = (uint16_t) (6u + strlen(target_url) + 2u);
|
||||
|
||||
/* Provide space for the data-buf */
|
||||
if (!(srv_data = (pico_dns_srv_record *) PICO_ZALLOC(srv_length))) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set the fields */
|
||||
srv_data->priority = short_be(priority);
|
||||
srv_data->weight = short_be(weight);
|
||||
srv_data->port = short_be(port);
|
||||
|
||||
/* Copy in the URL and convert to DNS notation */
|
||||
if (!(target_rname = pico_dns_url_to_qname(target_url))) {
|
||||
dns_sd_dbg("Could not convert URL to qname!\n");
|
||||
PICO_FREE(srv_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy((char *)srv_data + 6u, target_rname);
|
||||
PICO_FREE(target_rname);
|
||||
|
||||
/* Create and return new mDNS record */
|
||||
record = pico_mdns_record_create(url, srv_data, srv_length,
|
||||
PICO_DNS_TYPE_SRV,
|
||||
ttl, flags);
|
||||
PICO_FREE(srv_data);
|
||||
return record;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Creates an mDNS record with the TXT record format.
|
||||
*
|
||||
* @param url Name of the TXT record in URL format.
|
||||
* @param key_value_pairs Key-Value pair vector to generate the data from.
|
||||
* @param ttl TTL of the TXT record.
|
||||
* @param flags mDNS record flags to set specifications of the record
|
||||
* @return Pointer to newly created record on success, NULL on failure.
|
||||
* ****************************************************************************/
|
||||
static struct pico_mdns_record *
|
||||
pico_dns_sd_txt_record_create( const char *url,
|
||||
kv_vector key_value_pairs,
|
||||
uint32_t ttl,
|
||||
uint8_t flags )
|
||||
{
|
||||
struct pico_mdns_record *record = NULL;
|
||||
key_value_pair_t *iterator = NULL;
|
||||
char *txt = NULL;
|
||||
uint16_t i = 0, txt_i = 0, pair_len = 0, key_len = 0, value_len = 0;
|
||||
|
||||
/* Determine the length of the string to fit in all pairs */
|
||||
uint16_t len = (uint16_t)(pico_dns_sd_kv_vector_strlen(&key_value_pairs) + 1u);
|
||||
|
||||
/* If kv-vector is empty don't bother to create a TXT record */
|
||||
if (len <= 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Provide space for the txt buf */
|
||||
if (!(txt = (char *)PICO_ZALLOC(len))) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Iterate over all the key-value pairs */
|
||||
for (i = 0; i < key_value_pairs.count; i++) {
|
||||
iterator = pico_dns_sd_kv_vector_get(&key_value_pairs, i);
|
||||
|
||||
/* Determine the length of the key */
|
||||
key_len = (uint16_t) strlen(iterator->key);
|
||||
pair_len = key_len;
|
||||
|
||||
/* If value is not a NULL-ptr */
|
||||
if (iterator->value) {
|
||||
value_len = (uint16_t) strlen(iterator->value);
|
||||
pair_len = (uint16_t) (pair_len + 1u + value_len);
|
||||
}
|
||||
|
||||
/* Set the pair length label */
|
||||
txt[txt_i] = (char)pair_len;
|
||||
|
||||
/* Copy the key */
|
||||
strcpy(txt + txt_i + 1u, iterator->key);
|
||||
|
||||
/* Copy the value if it is not a NULL-ptr */
|
||||
if (iterator->value) {
|
||||
strcpy(txt + txt_i + 1u + key_len, "=");
|
||||
strcpy(txt + txt_i + 2u + key_len, iterator->value);
|
||||
txt_i = (uint16_t) (txt_i + 2u + key_len + value_len);
|
||||
} else {
|
||||
txt_i = (uint16_t) (txt_i + 1u + key_len);
|
||||
}
|
||||
}
|
||||
record = pico_mdns_record_create(url, txt, (uint16_t)(len - 1u), PICO_DNS_TYPE_TXT, ttl, flags);
|
||||
PICO_FREE(txt);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Deletes a single key-value pair instance
|
||||
*
|
||||
* @param kv_pair Pointer-pointer to to delete instance
|
||||
* @return Returns 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
static int
|
||||
pico_dns_sd_kv_delete( key_value_pair_t **kv_pair )
|
||||
{
|
||||
/* Check params */
|
||||
if (!kv_pair || !(*kv_pair)) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Delete the fields */
|
||||
if ((*kv_pair)->key)
|
||||
PICO_FREE((*kv_pair)->key);
|
||||
|
||||
if ((*kv_pair)->value)
|
||||
PICO_FREE((*kv_pair)->value);
|
||||
|
||||
PICO_FREE(*kv_pair);
|
||||
*kv_pair = NULL;
|
||||
kv_pair = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Creates a single key-value pair-instance
|
||||
*
|
||||
* @param key Key of the pair, cannot be NULL.
|
||||
* @param value Value of the pair, can be NULL, empty ("") or filled ("qkejq")
|
||||
* @return Pointer to newly created KV-instance on success, NULL on failure.
|
||||
* ****************************************************************************/
|
||||
static key_value_pair_t *
|
||||
pico_dns_sd_kv_create( const char *key, const char *value )
|
||||
{
|
||||
key_value_pair_t *kv_pair = NULL;
|
||||
|
||||
/* Check params */
|
||||
if (!key || !(kv_pair = PICO_ZALLOC(sizeof(key_value_pair_t)))) {
|
||||
pico_dns_sd_kv_delete(&kv_pair);
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Provide space to copy the values */
|
||||
if (!(kv_pair->key = PICO_ZALLOC((size_t)(strlen(key) + 1)))) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
pico_dns_sd_kv_delete(&kv_pair);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(kv_pair->key, key);
|
||||
|
||||
if (value) {
|
||||
if (!(kv_pair->value = PICO_ZALLOC((size_t)(strlen(value) + 1)))) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
pico_dns_sd_kv_delete(&kv_pair);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(kv_pair->value, value);
|
||||
} else
|
||||
kv_pair->value = NULL;
|
||||
|
||||
return kv_pair;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Checks whether the type is correctly formatted ant it's label length are
|
||||
* between the allowed boundaries.
|
||||
*
|
||||
* @param type Servicetype to check the format of.
|
||||
* @return Returns 0 when the type is correctly formatted, something else when
|
||||
* it's not.
|
||||
* ****************************************************************************/
|
||||
static int
|
||||
pico_dns_sd_check_type_format( const char *type )
|
||||
{
|
||||
uint16_t first_lbl = 0;
|
||||
int8_t subtype_present = 0;
|
||||
|
||||
/* Check params */
|
||||
if (!(first_lbl = pico_dns_first_label_length(type)))
|
||||
return -1;
|
||||
|
||||
subtype_present = !memcmp(type + first_lbl + 1, "_sub", 4);
|
||||
|
||||
/* Check if there is a subtype present */
|
||||
if (subtype_present && (first_lbl > 63))
|
||||
return -1;
|
||||
else if (subtype_present)
|
||||
/* Get the length of the service name */
|
||||
first_lbl = pico_dns_first_label_length(type + first_lbl + 6);
|
||||
else {
|
||||
/* Check if type is not greater then 21 bytes (22 - 1, since the length
|
||||
byte of the service name isn't included yet) */
|
||||
if (strlen(type) > (size_t) 21)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the service name is not greater then 16 bytes (17 - 1) */
|
||||
return (first_lbl > ((uint16_t) 16u));
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Checks whether the service instance name is correctly formatted and it's
|
||||
* label length falls between the allowed boundaries.
|
||||
*
|
||||
* @param name Instance name to check the format of.
|
||||
* @return Returns 0 when the name is correctly formatted, something else when
|
||||
* it's not.
|
||||
* ****************************************************************************/
|
||||
static int
|
||||
pico_dns_sd_check_instance_name_format( const char *name )
|
||||
{
|
||||
/* First of all check if the total length is larger than 63 bytes */
|
||||
if (pico_dns_strlen(name) > 63 || !pico_dns_strlen(name))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Append the instance name adn service type to create a '.local' service SIN.
|
||||
*
|
||||
* @param name Instance Name of the service, f.e. "Printer 2nd Floor".
|
||||
* @param type ServiceType of the service, f.e. "_http._tcp".
|
||||
* @return Pointer to newly created SIN on success, NULL on failure.
|
||||
* ****************************************************************************/
|
||||
static char *
|
||||
pico_dns_sd_create_service_url( const char *name,
|
||||
const char *type )
|
||||
{
|
||||
char *url = NULL;
|
||||
uint16_t len = 0, namelen = 0, typelen = 0;
|
||||
|
||||
if (pico_dns_sd_check_type_format(type)) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pico_dns_sd_check_instance_name_format(name)) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
namelen = (uint16_t)strlen(name);
|
||||
typelen = (uint16_t)strlen(type);
|
||||
|
||||
/* Determine the length that the URL needs to be */
|
||||
len = (uint16_t)(namelen + 1u /* for '.'*/ +
|
||||
typelen + 7u /* for '.local\0' */);
|
||||
url = (char *)PICO_ZALLOC(len);
|
||||
if (!url) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Append the parts together */
|
||||
strcpy(url, name);
|
||||
strcpy(url + namelen, ".");
|
||||
strcpy(url + namelen + 1, type);
|
||||
strcpy(url + namelen + 1 + typelen, ".local");
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* This function actually does exactly the same as pico_mdns_init();
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_init( const char *_hostname,
|
||||
struct pico_ip4 address,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg )
|
||||
{
|
||||
return pico_mdns_init(_hostname, address, callback, arg);
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Just calls pico_mdns_init in its turn to initialise the mDNS-module.
|
||||
* See pico_mdns.h for description.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_register_service( const char *name,
|
||||
const char *type,
|
||||
uint16_t port,
|
||||
kv_vector *txt_data,
|
||||
uint16_t ttl,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg)
|
||||
{
|
||||
PICO_MDNS_RTREE_DECLARE(rtree);
|
||||
struct pico_mdns_record *srv_record = NULL;
|
||||
struct pico_mdns_record *txt_record = NULL;
|
||||
const char *hostname = pico_mdns_get_hostname();
|
||||
char *url = NULL;
|
||||
|
||||
/* Try to create a service URL to create records with */
|
||||
if (!(url = pico_dns_sd_create_service_url(name, type)) || !txt_data || !hostname) {
|
||||
if (url) {
|
||||
PICO_FREE(url);
|
||||
}
|
||||
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dns_sd_dbg("\n>>>>>>>>>> Target: %s <<<<<<<<<<\n\n", hostname);
|
||||
|
||||
/* Create the SRV record */
|
||||
srv_record = pico_dns_sd_srv_record_create(url, 0, 0, port, hostname, ttl, PICO_MDNS_RECORD_UNIQUE);
|
||||
if (!srv_record) {
|
||||
PICO_FREE(url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create the TXT record */
|
||||
txt_record = pico_dns_sd_txt_record_create(url, *txt_data, ttl, PICO_MDNS_RECORD_UNIQUE);
|
||||
PICO_FREE(url);
|
||||
|
||||
/* Erase the key-value pair vector, it's no longer needed */
|
||||
pico_dns_sd_kv_vector_erase(txt_data);
|
||||
|
||||
if (txt_record) {
|
||||
if (pico_tree_insert(&rtree, txt_record) == &LEAF) {
|
||||
PICO_MDNS_RTREE_DESTROY(&rtree);
|
||||
pico_mdns_record_delete((void **)&txt_record);
|
||||
pico_mdns_record_delete((void **)&srv_record);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pico_tree_insert(&rtree, srv_record) == &LEAF) {
|
||||
PICO_MDNS_RTREE_DESTROY(&rtree);
|
||||
pico_mdns_record_delete((void **)&srv_record);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_mdns_claim(rtree, callback, arg)) {
|
||||
PICO_MDNS_RTREE_DESTROY(&rtree);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Only destroy the tree, not its elements since they still exist in another tree */
|
||||
pico_tree_destroy(&rtree, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Does nothing for now.
|
||||
*
|
||||
* @param type Type to browse for.
|
||||
* @param callback Callback to call when something particular happens.
|
||||
* @return When the module successfully started browsing the servicetype.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_browse_service( const char *type,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg )
|
||||
{
|
||||
IGNORE_PARAMETER(type);
|
||||
IGNORE_PARAMETER(callback);
|
||||
IGNORE_PARAMETER(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Add a key-value pair the a key-value pair vector.
|
||||
*
|
||||
* @param vector Vector to add the pair to.
|
||||
* @param key Key of the pair, cannot be NULL.
|
||||
* @param value Value of the pair, can be NULL, empty ("") or filled ("qkejq")
|
||||
* @return Returns 0 when the pair is added successfully, something else on
|
||||
* failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_kv_vector_add( kv_vector *vector, char *key, char *value )
|
||||
{
|
||||
key_value_pair_t *kv_pair = NULL;
|
||||
key_value_pair_t **new_pairs = NULL;
|
||||
uint16_t i = 0;
|
||||
|
||||
/* Check params */
|
||||
if (!vector || !key || !(kv_pair = pico_dns_sd_kv_create(key, value))) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
pico_dns_sd_kv_delete(&kv_pair);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Provide enough space for the new pair pointers */
|
||||
if (!(new_pairs = PICO_ZALLOC(sizeof(key_value_pair_t *) *
|
||||
(vector->count + 1u)))) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
pico_dns_sd_kv_delete(&kv_pair);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy previous pairs and add new one */
|
||||
for (i = 0; i < vector->count; i++)
|
||||
new_pairs[i] = vector->pairs[i];
|
||||
new_pairs[i] = kv_pair;
|
||||
|
||||
/* Free the previous array */
|
||||
if (vector->pairs)
|
||||
PICO_FREE(vector->pairs);
|
||||
|
||||
vector->pairs = new_pairs;
|
||||
vector->count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Gets a single key-value pair form a Key-Value pair vector @ certain index.
|
||||
*
|
||||
* @param vector Vector to get KV-pair from.
|
||||
* @param index Index of the KV-pair.
|
||||
* @return key_value_pair_t* on success, NULL on failure.
|
||||
* ****************************************************************************/
|
||||
key_value_pair_t *
|
||||
pico_dns_sd_kv_vector_get( kv_vector *vector, uint16_t index )
|
||||
{
|
||||
/* Check params */
|
||||
if (!vector)
|
||||
return NULL;
|
||||
|
||||
/* Return record with conditioned index */
|
||||
if (index < vector->count)
|
||||
return vector->pairs[index];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Erase all the contents of a key-value pair vector.
|
||||
*
|
||||
* @param vector Key-Value pair vector.
|
||||
* @return 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_kv_vector_erase( kv_vector *vector )
|
||||
{
|
||||
uint16_t i = 0;
|
||||
|
||||
/* Iterate over each key-value pair */
|
||||
for (i = 0; i < vector->count; i++) {
|
||||
if (pico_dns_sd_kv_delete(&(vector->pairs[i])) < 0) {
|
||||
dns_sd_dbg("Could not delete key-value pairs from vector");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
PICO_FREE(vector->pairs);
|
||||
vector->pairs = NULL;
|
||||
vector->count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
91
kernel/picotcp/modules/pico_dns_sd.h
Normal file
91
kernel/picotcp/modules/pico_dns_sd.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* ****************************************************************************
|
||||
* PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved.
|
||||
* See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
* .
|
||||
* Author: Jelle De Vleeschouwer
|
||||
* ****************************************************************************/
|
||||
#ifndef INCLUDE_PICO_DNS_SD
|
||||
#define INCLUDE_PICO_DNS_SD
|
||||
|
||||
#include "pico_mdns.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *key;
|
||||
char *value;
|
||||
} key_value_pair_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
key_value_pair_t **pairs;
|
||||
uint16_t count;
|
||||
} kv_vector;
|
||||
|
||||
#define PICO_DNS_SD_KV_VECTOR_DECLARE(name) \
|
||||
kv_vector (name) = {0}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Just calls pico_mdns_init in it's turn to initialise the mDNS-module.
|
||||
* See pico_mdns.h for description.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_init( const char *_hostname,
|
||||
struct pico_ip4 address,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Register a DNS-SD service via Multicast DNS on the local network.
|
||||
*
|
||||
* @param name Instance Name of the service, f.e. "Printer 2nd Floor".
|
||||
* @param type ServiceType of the service, f.e. "_http._tcp".
|
||||
* @param port Port number on which the service runs.
|
||||
* @param txt_data TXT data to create TXT record with, need kv_vector-type,
|
||||
* Declare such a type with PICO_DNS_SD_KV_VECTOR_DECLARE(*) &
|
||||
* add key-value pairs with pico_dns_sd_kv_vector_add().
|
||||
* @param ttl TTL
|
||||
* @param callback Callback-function to call when the service is registered.
|
||||
* @return
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_register_service( const char *name,
|
||||
const char *type,
|
||||
uint16_t port,
|
||||
kv_vector *txt_data,
|
||||
uint16_t ttl,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg);
|
||||
|
||||
/* ****************************************************************************
|
||||
* Does nothing for now.
|
||||
*
|
||||
* @param type Type to browse for.
|
||||
* @param callback Callback to call when something particular happens.
|
||||
* @return When the module successfully started browsing the servicetype.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_browse_service( const char *type,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Add a key-value pair the a key-value pair vector.
|
||||
*
|
||||
* @param vector Vector to add the pair to.
|
||||
* @param key Key of the pair, cannot be NULL.
|
||||
* @param value Value of the pair, can be NULL, empty ("") or filled ("qkejq")
|
||||
* @return Returns 0 when the pair is added successfully, something else on
|
||||
* failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_dns_sd_kv_vector_add( kv_vector *vector, char *key, char *value );
|
||||
|
||||
|
||||
#endif /* _INCLUDE_PICO_DNS_SD */
|
||||
|
||||
448
kernel/picotcp/modules/pico_ethernet.c
Normal file
448
kernel/picotcp/modules/pico_ethernet.c
Normal file
@ -0,0 +1,448 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_icmp4.h"
|
||||
#include "pico_icmp6.h"
|
||||
#include "pico_arp.h"
|
||||
#include "pico_ethernet.h"
|
||||
|
||||
#define IS_LIMITED_BCAST(f) (((struct pico_ipv4_hdr *) f->net_hdr)->dst.addr == PICO_IP4_BCAST)
|
||||
|
||||
#ifdef PICO_SUPPORT_ETH
|
||||
|
||||
const uint8_t PICO_ETHADDR_ALL[6] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
# define PICO_SIZE_MCAST 3
|
||||
static const uint8_t PICO_ETHADDR_MCAST[6] = {
|
||||
0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
# define PICO_SIZE_MCAST6 2
|
||||
static const uint8_t PICO_ETHADDR_MCAST6[6] = {
|
||||
0x33, 0x33, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
/* DATALINK LEVEL: interface from network to the device
|
||||
* and vice versa.
|
||||
*/
|
||||
|
||||
/* The pico_ethernet_receive() function is used by
|
||||
* those devices supporting ETH in order to push packets up
|
||||
* into the stack.
|
||||
*/
|
||||
|
||||
/* Queues */
|
||||
static struct pico_queue ethernet_in = {
|
||||
0
|
||||
};
|
||||
static struct pico_queue ethernet_out = {
|
||||
0
|
||||
};
|
||||
|
||||
int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f);
|
||||
static int32_t pico_ethernet_receive(struct pico_frame *f);
|
||||
|
||||
static int pico_ethernet_process_out(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(self);
|
||||
return pico_ethernet_send(f);
|
||||
}
|
||||
|
||||
static int pico_ethernet_process_in(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(self);
|
||||
return (pico_ethernet_receive(f) <= 0); /* 0 on success, which is ret > 0 */
|
||||
}
|
||||
|
||||
static struct pico_frame *pico_ethernet_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size)
|
||||
{
|
||||
struct pico_frame *f = NULL;
|
||||
uint32_t overhead = 0;
|
||||
IGNORE_PARAMETER(self);
|
||||
|
||||
if (dev)
|
||||
overhead = dev->overhead;
|
||||
|
||||
f = pico_frame_alloc((uint32_t)(overhead + size + PICO_SIZE_ETHHDR));
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
f->dev = dev;
|
||||
f->datalink_hdr = f->buffer + overhead;
|
||||
f->net_hdr = f->datalink_hdr + PICO_SIZE_ETHHDR;
|
||||
/* Stay of the rest, higher levels will take care */
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/* Interface: protocol definition */
|
||||
struct pico_protocol pico_proto_ethernet = {
|
||||
.name = "ethernet",
|
||||
.layer = PICO_LAYER_DATALINK,
|
||||
.alloc = pico_ethernet_alloc,
|
||||
.process_in = pico_ethernet_process_in,
|
||||
.process_out = pico_ethernet_process_out,
|
||||
.q_in = ðernet_in,
|
||||
.q_out = ðernet_out,
|
||||
};
|
||||
|
||||
static int destination_is_bcast(struct pico_frame *f)
|
||||
{
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
if (IS_IPV6(f))
|
||||
return 0;
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
else {
|
||||
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
|
||||
return pico_ipv4_is_broadcast(hdr->dst.addr);
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int destination_is_mcast(struct pico_frame *f)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
if (IS_IPV6(f)) {
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *) f->net_hdr;
|
||||
ret = pico_ipv6_is_multicast(hdr->dst.addr);
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
else {
|
||||
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
|
||||
ret = pico_ipv4_is_multicast(hdr->dst.addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
static int32_t pico_ipv4_ethernet_receive(struct pico_frame *f)
|
||||
{
|
||||
if (IS_IPV4(f)) {
|
||||
if (pico_enqueue(pico_proto_ipv4.q_in, f) < 0) {
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
(void)pico_icmp4_param_problem(f, 0);
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int32_t)f->buffer_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
static int32_t pico_ipv6_ethernet_receive(struct pico_frame *f)
|
||||
{
|
||||
if (IS_IPV6(f)) {
|
||||
if (pico_enqueue(pico_proto_ipv6.q_in, f) < 0) {
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* Wrong version for link layer type */
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int32_t)f->buffer_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int32_t pico_eth_receive(struct pico_frame *f)
|
||||
{
|
||||
struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
|
||||
f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr);
|
||||
|
||||
#if (defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH)
|
||||
if (hdr->proto == PICO_IDETH_ARP)
|
||||
return pico_arp_receive(f);
|
||||
#endif
|
||||
|
||||
#if defined (PICO_SUPPORT_IPV4)
|
||||
if (hdr->proto == PICO_IDETH_IPV4)
|
||||
return pico_ipv4_ethernet_receive(f);
|
||||
#endif
|
||||
|
||||
#if defined (PICO_SUPPORT_IPV6)
|
||||
if (hdr->proto == PICO_IDETH_IPV6)
|
||||
return pico_ipv6_ethernet_receive(f);
|
||||
#endif
|
||||
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pico_eth_check_bcast(struct pico_frame *f)
|
||||
{
|
||||
struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
|
||||
/* Indicate a link layer broadcast packet */
|
||||
if (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) == 0)
|
||||
f->flags |= PICO_FRAME_FLAG_BCAST;
|
||||
}
|
||||
|
||||
static int32_t pico_ethernet_receive(struct pico_frame *f)
|
||||
{
|
||||
struct pico_eth_hdr *hdr;
|
||||
if (!f || !f->dev || !f->datalink_hdr)
|
||||
{
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = (struct pico_eth_hdr *) f->datalink_hdr;
|
||||
if ((memcmp(hdr->daddr, f->dev->eth->mac.addr, PICO_SIZE_ETH) != 0) &&
|
||||
(memcmp(hdr->daddr, PICO_ETHADDR_MCAST, PICO_SIZE_MCAST) != 0) &&
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
(memcmp(hdr->daddr, PICO_ETHADDR_MCAST6, PICO_SIZE_MCAST6) != 0) &&
|
||||
#endif
|
||||
(memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) != 0))
|
||||
{
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_eth_check_bcast(f);
|
||||
return pico_eth_receive(f);
|
||||
}
|
||||
|
||||
static struct pico_eth *pico_ethernet_mcast_translate(struct pico_frame *f, uint8_t *pico_mcast_mac)
|
||||
{
|
||||
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
|
||||
|
||||
/* place 23 lower bits of IP in lower 23 bits of MAC */
|
||||
pico_mcast_mac[5] = (long_be(hdr->dst.addr) & 0x000000FFu);
|
||||
pico_mcast_mac[4] = (uint8_t)((long_be(hdr->dst.addr) & 0x0000FF00u) >> 8u);
|
||||
pico_mcast_mac[3] = (uint8_t)((long_be(hdr->dst.addr) & 0x007F0000u) >> 16u);
|
||||
|
||||
return (struct pico_eth *)pico_mcast_mac;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
static struct pico_eth *pico_ethernet_mcast6_translate(struct pico_frame *f, uint8_t *pico_mcast6_mac)
|
||||
{
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
|
||||
/* first 2 octets are 0x33, last four are the last four of dst */
|
||||
pico_mcast6_mac[5] = hdr->dst.addr[PICO_SIZE_IP6 - 1];
|
||||
pico_mcast6_mac[4] = hdr->dst.addr[PICO_SIZE_IP6 - 2];
|
||||
pico_mcast6_mac[3] = hdr->dst.addr[PICO_SIZE_IP6 - 3];
|
||||
pico_mcast6_mac[2] = hdr->dst.addr[PICO_SIZE_IP6 - 4];
|
||||
|
||||
return (struct pico_eth *)pico_mcast6_mac;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const dstmac)
|
||||
{
|
||||
int retval = -1;
|
||||
if (!dstmac)
|
||||
return -1;
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
if (destination_is_mcast(f)) {
|
||||
uint8_t pico_mcast6_mac[6] = {
|
||||
0x33, 0x33, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
pico_ethernet_mcast6_translate(f, pico_mcast6_mac);
|
||||
memcpy(dstmac, pico_mcast6_mac, PICO_SIZE_ETH);
|
||||
retval = 0;
|
||||
} else {
|
||||
struct pico_eth *neighbor = pico_ipv6_get_neighbor(f);
|
||||
if (neighbor)
|
||||
{
|
||||
memcpy(dstmac, neighbor, PICO_SIZE_ETH);
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
(void)f;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* Ethernet send, first attempt: try our own address.
|
||||
* Returns 0 if the packet is not for us.
|
||||
* Returns 1 if the packet is cloned to our own receive queue and the original frame is dicarded.
|
||||
* */
|
||||
static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr)
|
||||
{
|
||||
if (!hdr)
|
||||
return 0;
|
||||
|
||||
/* Check own mac */
|
||||
if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) {
|
||||
struct pico_frame *clone = pico_frame_copy(f);
|
||||
dbg("sending out packet destined for our own mac\n");
|
||||
if (pico_ethernet_receive(clone) < 0) {
|
||||
dbg("pico_ethernet_receive() failed\n");
|
||||
return 0;
|
||||
}
|
||||
pico_frame_discard(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ethernet send, second attempt: try bcast.
|
||||
* Returns 0 if the packet is not bcast, so it will be handled somewhere else.
|
||||
* Returns 1 if the packet is handled by the pico_device_broadcast() function and is discarded.
|
||||
* */
|
||||
static int32_t pico_ethsend_bcast(struct pico_frame *f)
|
||||
{
|
||||
if (IS_LIMITED_BCAST(f)) {
|
||||
return (pico_device_broadcast(f) > 0); // Return 1 on success, ret > 0
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ethernet send, third attempt: try unicast.
|
||||
* If the device driver is busy, we return 0, so the stack won't discard the frame.
|
||||
* In case of success, we can safely return 1.
|
||||
*/
|
||||
static int32_t pico_ethsend_dispatch(struct pico_frame *f)
|
||||
{
|
||||
return (pico_sendto_dev(f) > 0); // Return 1 on success, ret > 0
|
||||
}
|
||||
|
||||
/* Checks whether or not there's enough headroom allocated in the frame to
|
||||
* prepend the Ethernet header. Reallocates if this is not the case. */
|
||||
static int eth_check_headroom(struct pico_frame *f)
|
||||
{
|
||||
uint32_t headroom = (uint32_t)(f->net_hdr - f->buffer);
|
||||
uint32_t grow = (uint32_t)(PICO_SIZE_ETHHDR - headroom);
|
||||
if (headroom < (uint32_t)PICO_SIZE_ETHHDR) {
|
||||
return pico_frame_grow_head(f, (uint32_t)(f->buffer_len + grow));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function looks for the destination mac address
|
||||
* in order to send the frame being processed.
|
||||
*/
|
||||
int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f)
|
||||
{
|
||||
struct pico_eth dstmac;
|
||||
uint8_t dstmac_valid = 0;
|
||||
uint16_t proto = PICO_IDETH_IPV4;
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
/* Step 1: If the frame has an IPv6 packet,
|
||||
* destination address is taken from the ND tables
|
||||
*/
|
||||
if (IS_IPV6(f)) {
|
||||
if (pico_ethernet_ipv6_dst(f, &dstmac) < 0)
|
||||
{
|
||||
/* Enqueue copy of frame in IPv6 ND-module to retry later. Discard
|
||||
* frame, otherwise we have a duplicate in IPv6-ND */
|
||||
pico_ipv6_nd_postpone(f);
|
||||
return (int32_t)f->len;
|
||||
}
|
||||
|
||||
dstmac_valid = 1;
|
||||
proto = PICO_IDETH_IPV6;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
/* In case of broadcast (IPV4 only), dst mac is FF:FF:... */
|
||||
if (IS_BCAST(f) || destination_is_bcast(f))
|
||||
{
|
||||
memcpy(&dstmac, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
|
||||
dstmac_valid = 1;
|
||||
}
|
||||
|
||||
/* In case of multicast, dst mac is translated from the group address */
|
||||
else if (destination_is_mcast(f)) {
|
||||
uint8_t pico_mcast_mac[6] = {
|
||||
0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
|
||||
};
|
||||
pico_ethernet_mcast_translate(f, pico_mcast_mac);
|
||||
memcpy(&dstmac, pico_mcast_mac, PICO_SIZE_ETH);
|
||||
dstmac_valid = 1;
|
||||
}
|
||||
|
||||
#if (defined PICO_SUPPORT_IPV4)
|
||||
else {
|
||||
struct pico_eth *arp_get;
|
||||
arp_get = pico_arp_get(f);
|
||||
if (arp_get) {
|
||||
memcpy(&dstmac, arp_get, PICO_SIZE_ETH);
|
||||
dstmac_valid = 1;
|
||||
} else {
|
||||
/* Enqueue copy of frame in ARP-module to retry later. Discard
|
||||
* frame otherwise we have a duplicate */
|
||||
pico_arp_postpone(f);
|
||||
return (int32_t)f->len;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This sets destination and source address, then pushes the packet to the device. */
|
||||
if (dstmac_valid) {
|
||||
struct pico_eth_hdr *hdr;
|
||||
if (!eth_check_headroom(f)) {
|
||||
hdr = (struct pico_eth_hdr *) f->datalink_hdr;
|
||||
if ((f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR))
|
||||
{
|
||||
f->start -= PICO_SIZE_ETHHDR;
|
||||
f->len += PICO_SIZE_ETHHDR;
|
||||
f->datalink_hdr = f->start;
|
||||
hdr = (struct pico_eth_hdr *) f->datalink_hdr;
|
||||
memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
memcpy(hdr->daddr, &dstmac, PICO_SIZE_ETH);
|
||||
hdr->proto = proto;
|
||||
}
|
||||
|
||||
if (pico_ethsend_local(f, hdr) || pico_ethsend_bcast(f) || pico_ethsend_dispatch(f)) {
|
||||
/* one of the above functions has delivered the frame accordingly.
|
||||
* (returned != 0). It is safe to directly return successfully.
|
||||
* Lower level queue has frame, so don't discard */
|
||||
return (int32_t)f->len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Failure, frame could not be be enqueued in lower-level layer, safe
|
||||
* to discard since something clearly went wrong */
|
||||
pico_frame_discard(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* PICO_SUPPORT_ETH */
|
||||
|
||||
|
||||
18
kernel/picotcp/modules/pico_ethernet.h
Normal file
18
kernel/picotcp/modules/pico_ethernet.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_ETHERNET
|
||||
#define INCLUDE_PICO_ETHERNET
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_frame.h"
|
||||
|
||||
extern struct pico_protocol pico_proto_ethernet;
|
||||
|
||||
#endif /* INCLUDE_PICO_ETHERNET */
|
||||
573
kernel/picotcp/modules/pico_fragments.c
Normal file
573
kernel/picotcp/modules/pico_fragments.c
Normal file
@ -0,0 +1,573 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Laurens Miers, Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_config.h"
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_icmp6.h"
|
||||
#endif
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_icmp4.h"
|
||||
#endif
|
||||
#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_tree.h"
|
||||
#include "pico_constants.h"
|
||||
#include "pico_fragments.h"
|
||||
|
||||
#ifdef DEBUG_FRAG
|
||||
#define frag_dbg dbg
|
||||
#else
|
||||
#define frag_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
#define IP6_FRAG_OFF(x) ((x & 0xFFF8u))
|
||||
#define IP6_FRAG_MORE(x) ((x & 0x0001))
|
||||
#define IP6_FRAG_ID(x) ((uint32_t)(((uint32_t)x->ext.frag.id[0] << 24) + ((uint32_t)x->ext.frag.id[1] << 16) + \
|
||||
((uint32_t)x->ext.frag.id[2] << 8) + (uint32_t)x->ext.frag.id[3]))
|
||||
|
||||
#else
|
||||
#define IP6_FRAG_OFF(x) (0)
|
||||
#define IP6_FRAG_MORE(x) (0)
|
||||
#define IP6_FRAG_ID(x) (0)
|
||||
#endif
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
#define IP4_FRAG_OFF(frag) (((uint32_t)frag & PICO_IPV4_FRAG_MASK) << 3ul)
|
||||
#define IP4_FRAG_MORE(frag) ((frag & PICO_IPV4_MOREFRAG) ? 1 : 0)
|
||||
#define IP4_FRAG_ID(hdr) (hdr->id)
|
||||
#else
|
||||
#define IP4_FRAG_OFF(frag) (0)
|
||||
#define IP4_FRAG_MORE(frag) (0)
|
||||
#define IP4_FRAG_ID(hdr) (0)
|
||||
#endif
|
||||
|
||||
#define PICO_IPV6_FRAG_TIMEOUT 60000
|
||||
#define PICO_IPV4_FRAG_TIMEOUT 15000
|
||||
|
||||
static void pico_frag_expire(pico_time now, void *arg);
|
||||
static void pico_fragments_complete(unsigned int bookmark, uint8_t proto, uint8_t net);
|
||||
static int pico_fragments_check_complete(struct pico_tree *tree, uint8_t proto, uint8_t net);
|
||||
static int pico_fragments_reassemble(struct pico_tree *tree, unsigned int len, uint8_t proto, uint8_t net);
|
||||
static int pico_fragments_get_more_flag(struct pico_frame *frame, uint8_t net);
|
||||
static uint32_t pico_fragments_get_offset(struct pico_frame *frame, uint8_t net);
|
||||
static void pico_fragments_send_notify(struct pico_frame *first);
|
||||
static uint16_t pico_fragments_get_header_length(uint8_t net);
|
||||
static void pico_fragments_empty_tree(struct pico_tree *tree);
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
static uint32_t ipv6_cur_frag_id = 0u;
|
||||
static uint32_t ipv6_fragments_timer = 0u;
|
||||
|
||||
static int pico_ipv6_frag_compare(void *ka, void *kb)
|
||||
{
|
||||
struct pico_frame *a = ka, *b = kb;
|
||||
if (IP6_FRAG_OFF(a->frag) > IP6_FRAG_OFF(b->frag))
|
||||
return 1;
|
||||
|
||||
if (IP6_FRAG_OFF(a->frag) < IP6_FRAG_OFF(b->frag))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static PICO_TREE_DECLARE(ipv6_fragments, pico_ipv6_frag_compare);
|
||||
|
||||
static void pico_ipv6_fragments_complete(unsigned int len, uint8_t proto)
|
||||
{
|
||||
if (pico_fragments_reassemble(&ipv6_fragments, len, proto, PICO_PROTO_IPV6) == 0)
|
||||
{
|
||||
pico_timer_cancel(ipv6_fragments_timer);
|
||||
ipv6_fragments_timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pico_ipv6_frag_timer_on(void)
|
||||
{
|
||||
ipv6_fragments_timer = pico_timer_add(PICO_IPV6_FRAG_TIMEOUT, pico_frag_expire, &ipv6_fragments);
|
||||
if (!ipv6_fragments_timer) {
|
||||
frag_dbg("FRAG: Failed to start IPv6 expiration timer\n");
|
||||
pico_fragments_empty_tree(&ipv6_fragments);
|
||||
}
|
||||
}
|
||||
|
||||
static int pico_ipv6_frag_match(struct pico_frame *a, struct pico_frame *b)
|
||||
{
|
||||
struct pico_ipv6_hdr *ha = NULL, *hb = NULL;
|
||||
if (!a || !b)
|
||||
return -1;
|
||||
|
||||
ha = (struct pico_ipv6_hdr *)a->net_hdr;
|
||||
hb = (struct pico_ipv6_hdr *)b->net_hdr;
|
||||
if (!ha || !hb)
|
||||
return -2;
|
||||
|
||||
if (memcmp(ha->src.addr, hb->src.addr, PICO_SIZE_IP6) != 0)
|
||||
return 1;
|
||||
|
||||
if (memcmp(ha->dst.addr, hb->dst.addr, PICO_SIZE_IP6) != 0)
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
static uint32_t ipv4_cur_frag_id = 0u;
|
||||
static uint32_t ipv4_fragments_timer = 0u;
|
||||
|
||||
static int pico_ipv4_frag_compare(void *ka, void *kb)
|
||||
{
|
||||
struct pico_frame *a = ka, *b = kb;
|
||||
if (IP4_FRAG_OFF(a->frag) > IP4_FRAG_OFF(b->frag))
|
||||
return 1;
|
||||
|
||||
if (IP4_FRAG_OFF(a->frag) < IP4_FRAG_OFF(b->frag))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static PICO_TREE_DECLARE(ipv4_fragments, pico_ipv4_frag_compare);
|
||||
|
||||
static void pico_ipv4_fragments_complete(unsigned int len, uint8_t proto)
|
||||
{
|
||||
if (pico_fragments_reassemble(&ipv4_fragments, len, proto, PICO_PROTO_IPV4) == 0)
|
||||
{
|
||||
pico_timer_cancel(ipv4_fragments_timer);
|
||||
ipv4_fragments_timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pico_ipv4_frag_timer_on(void)
|
||||
{
|
||||
ipv4_fragments_timer = pico_timer_add( PICO_IPV4_FRAG_TIMEOUT, pico_frag_expire, &ipv4_fragments);
|
||||
if (!ipv4_fragments_timer) {
|
||||
frag_dbg("FRAG: Failed to start IPv4 expiration timer\n");
|
||||
pico_fragments_empty_tree(&ipv4_fragments);
|
||||
}
|
||||
}
|
||||
|
||||
static int pico_ipv4_frag_match(struct pico_frame *a, struct pico_frame *b)
|
||||
{
|
||||
struct pico_ipv4_hdr *ha, *hb;
|
||||
if (!a || !b)
|
||||
return -1;
|
||||
|
||||
ha = (struct pico_ipv4_hdr *)a->net_hdr;
|
||||
hb = (struct pico_ipv4_hdr *)b->net_hdr;
|
||||
if (!ha || !hb)
|
||||
return -2;
|
||||
|
||||
if (memcmp(&(ha->src.addr), &(hb->src.addr), PICO_SIZE_IP4) != 0)
|
||||
return 1;
|
||||
|
||||
if (memcmp(&(ha->dst.addr), &(hb->dst.addr), PICO_SIZE_IP4) != 0)
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pico_fragments_complete(unsigned int bookmark, uint8_t proto, uint8_t net)
|
||||
{
|
||||
if (0) {}
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
else if (net == PICO_PROTO_IPV4)
|
||||
{
|
||||
pico_ipv4_fragments_complete(bookmark, proto);
|
||||
}
|
||||
#endif
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
else if (net == PICO_PROTO_IPV6)
|
||||
{
|
||||
pico_ipv6_fragments_complete(bookmark, proto);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void pico_fragments_empty_tree(struct pico_tree *tree)
|
||||
{
|
||||
struct pico_tree_node *index, *tmp;
|
||||
|
||||
if (!tree)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pico_tree_foreach_safe(index, tree, tmp) {
|
||||
struct pico_frame * old = index->keyValue;
|
||||
pico_tree_delete(tree, old);
|
||||
pico_frame_discard(old);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int pico_fragments_check_complete(struct pico_tree *tree, uint8_t proto, uint8_t net)
|
||||
{
|
||||
struct pico_tree_node *index, *temp;
|
||||
struct pico_frame *cur;
|
||||
unsigned int bookmark = 0;
|
||||
|
||||
if (!tree)
|
||||
return 0;
|
||||
|
||||
pico_tree_foreach_safe(index, tree, temp) {
|
||||
cur = index->keyValue;
|
||||
if (cur) {
|
||||
if (pico_fragments_get_offset(cur, net) != bookmark)
|
||||
return -1;
|
||||
|
||||
bookmark += cur->transport_len;
|
||||
if (!pico_fragments_get_more_flag(cur, net)) {
|
||||
pico_fragments_complete(bookmark, proto, net);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pico_frag_expire(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_tree *tree = (struct pico_tree *) arg;
|
||||
struct pico_frame *first = NULL;
|
||||
IGNORE_PARAMETER(now);
|
||||
|
||||
if (!tree)
|
||||
{
|
||||
frag_dbg("Expired packet but no tree supplied!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
first = pico_tree_first(tree);
|
||||
|
||||
if (!first) {
|
||||
frag_dbg("Empty tree - not sending notify\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pico_fragments_send_notify(first);
|
||||
|
||||
pico_fragments_empty_tree(tree);
|
||||
}
|
||||
|
||||
static void pico_fragments_send_notify(struct pico_frame *first)
|
||||
{
|
||||
uint8_t net = 0;
|
||||
|
||||
if (!first)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (0) {}
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
else if (IS_IPV4(first))
|
||||
{
|
||||
net = PICO_PROTO_IPV4;
|
||||
frag_dbg("Packet expired! ID:%hu\n", ipv4_cur_frag_id);
|
||||
}
|
||||
|
||||
#endif
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
else if (IS_IPV6(first))
|
||||
{
|
||||
net = PICO_PROTO_IPV6;
|
||||
frag_dbg("Packet expired! ID:%hu\n", ipv6_cur_frag_id);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (((pico_fragments_get_offset(first, net) == 0) && (pico_frame_dst_is_unicast(first))))
|
||||
{
|
||||
frag_dbg("sending notify\n");
|
||||
pico_notify_frag_expired(first);
|
||||
}
|
||||
else
|
||||
{
|
||||
frag_dbg("Not first packet or not unicast address, not sending notify");
|
||||
}
|
||||
}
|
||||
|
||||
static int pico_fragments_reassemble(struct pico_tree *tree, unsigned int len, uint8_t proto, uint8_t net)
|
||||
{
|
||||
struct pico_tree_node *index, *tmp;
|
||||
struct pico_frame *f;
|
||||
uint16_t header_length = 0;
|
||||
unsigned int bookmark = 0;
|
||||
struct pico_frame *full = NULL;
|
||||
struct pico_frame *first = NULL;
|
||||
|
||||
if (!tree)
|
||||
{
|
||||
frag_dbg("Cannot reassemble packet, no tree supplied!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
first = pico_tree_first(tree);
|
||||
|
||||
if (!first)
|
||||
{
|
||||
frag_dbg("Cannot reassemble packet, empty tree supplied!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
header_length = pico_fragments_get_header_length(net);
|
||||
|
||||
if (!header_length)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
full = pico_frame_alloc((uint16_t)(header_length + len));
|
||||
if (full) {
|
||||
full->net_hdr = full->buffer;
|
||||
full->net_len = header_length;
|
||||
memcpy(full->net_hdr, first->net_hdr, full->net_len);
|
||||
full->transport_hdr = full->net_hdr + full->net_len;
|
||||
full->transport_len = (uint16_t)len;
|
||||
full->dev = first->dev;
|
||||
pico_tree_foreach_safe(index, tree, tmp) {
|
||||
f = index->keyValue;
|
||||
memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len);
|
||||
bookmark += f->transport_len;
|
||||
pico_tree_delete(tree, f);
|
||||
pico_frame_discard(f);
|
||||
}
|
||||
if (pico_transport_receive(full, proto) == -1)
|
||||
{
|
||||
pico_frame_discard(full);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint16_t pico_fragments_get_header_length(uint8_t net)
|
||||
{
|
||||
if (0) {}
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
else if (net == PICO_PROTO_IPV4)
|
||||
{
|
||||
return PICO_SIZE_IP4HDR;
|
||||
}
|
||||
#endif
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
else if (net == PICO_PROTO_IPV6)
|
||||
{
|
||||
return PICO_SIZE_IP6HDR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_fragments_get_more_flag(struct pico_frame *frame, uint8_t net)
|
||||
{
|
||||
if (!frame)
|
||||
{
|
||||
frag_dbg("no frame given to determine more flag\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0) {}
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
else if (net == PICO_PROTO_IPV4)
|
||||
{
|
||||
return IP4_FRAG_MORE(frame->frag);
|
||||
}
|
||||
#endif
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
else if (net == PICO_PROTO_IPV6)
|
||||
{
|
||||
return IP6_FRAG_MORE(frame->frag);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t pico_fragments_get_offset(struct pico_frame *frame, uint8_t net)
|
||||
{
|
||||
if (!frame)
|
||||
{
|
||||
frag_dbg("no frame given to determine offset\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0) {}
|
||||
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
else if (net == PICO_PROTO_IPV4)
|
||||
{
|
||||
return IP4_FRAG_OFF(frame->frag);
|
||||
}
|
||||
#endif
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
else if (net == PICO_PROTO_IPV6)
|
||||
{
|
||||
return IP6_FRAG_OFF(frame->frag);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto)
|
||||
{
|
||||
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
|
||||
struct pico_frame *first = NULL;
|
||||
|
||||
if (!f || !frag)
|
||||
{
|
||||
frag_dbg("Bad arguments provided to pico_ipv6_process_frag\n");
|
||||
return;
|
||||
}
|
||||
|
||||
first = pico_tree_first(&ipv6_fragments);
|
||||
|
||||
if (first)
|
||||
{
|
||||
if ((pico_ipv6_frag_match(f, first) == 0 && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id))) {
|
||||
struct pico_frame *temp = NULL;
|
||||
|
||||
temp = pico_frame_copy(f);
|
||||
|
||||
if (!temp) {
|
||||
frag_dbg("Could not allocate memory to continue reassembly of IPV6 fragmented packet (id: %hu)\n", ipv6_cur_frag_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pico_tree_insert(&ipv6_fragments, temp)) {
|
||||
frag_dbg("FRAG: Could not insert picoframe in tree\n");
|
||||
pico_frame_discard(temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct pico_frame *temp = NULL;
|
||||
|
||||
if (ipv6_cur_frag_id && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id)) {
|
||||
/* Discard late arrivals, without firing the timer. */
|
||||
frag_dbg("discarded late arrival, exp:%hu found:%hu\n", ipv6_cur_frag_id, IP6_FRAG_ID(frag));
|
||||
return;
|
||||
}
|
||||
|
||||
temp = pico_frame_copy(f);
|
||||
|
||||
if (!temp) {
|
||||
frag_dbg("Could not allocate memory to start reassembly of fragmented packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pico_ipv6_frag_timer_on();
|
||||
ipv6_cur_frag_id = IP6_FRAG_ID(frag);
|
||||
frag_dbg("Started new reassembly, ID:%hu\n", ipv6_cur_frag_id);
|
||||
|
||||
if (pico_tree_insert(&ipv6_fragments, temp)) {
|
||||
frag_dbg("FRAG: Could not insert picoframe in tree\n");
|
||||
pico_frame_discard(temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pico_fragments_check_complete(&ipv6_fragments, proto, PICO_PROTO_IPV6);
|
||||
#else
|
||||
IGNORE_PARAMETER(frag);
|
||||
IGNORE_PARAMETER(f);
|
||||
IGNORE_PARAMETER(proto);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pico_ipv4_process_frag(struct pico_ipv4_hdr *hdr, struct pico_frame *f, uint8_t proto)
|
||||
{
|
||||
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
|
||||
struct pico_frame *first = NULL;
|
||||
|
||||
if (!f || !hdr)
|
||||
{
|
||||
frag_dbg("Bad arguments provided to pico_ipv4_process_frag\n");
|
||||
return;
|
||||
}
|
||||
|
||||
first = pico_tree_first(&ipv4_fragments);
|
||||
|
||||
if (first)
|
||||
{
|
||||
/* fragments from old packets still in tree, and new first fragment ? */
|
||||
if ((IP4_FRAG_ID(hdr) != ipv4_cur_frag_id) && (IP4_FRAG_OFF(f->frag) == 0)) {
|
||||
pico_fragments_empty_tree(&ipv4_fragments);
|
||||
|
||||
first = NULL;
|
||||
ipv4_cur_frag_id = 0;
|
||||
}
|
||||
|
||||
if ((pico_ipv4_frag_match(f, first) == 0 && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id))) {
|
||||
struct pico_frame *temp = NULL;
|
||||
|
||||
temp = pico_frame_copy(f);
|
||||
|
||||
if (!temp) {
|
||||
frag_dbg("Could not allocate memory to continue reassembly of IPV4 fragmented packet (id: %hu)\n", ipv4_cur_frag_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pico_tree_insert(&ipv4_fragments, temp)) {
|
||||
frag_dbg("FRAG: Could not insert picoframe in tree\n");
|
||||
pico_frame_discard(temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct pico_frame *temp = NULL;
|
||||
|
||||
if (ipv4_cur_frag_id && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id)) {
|
||||
/* Discard late arrivals, without firing the timer */
|
||||
return;
|
||||
}
|
||||
|
||||
temp = pico_frame_copy(f);
|
||||
|
||||
if (!temp) {
|
||||
frag_dbg("Could not allocate memory to start reassembly fragmented packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pico_ipv4_frag_timer_on();
|
||||
ipv4_cur_frag_id = IP4_FRAG_ID(hdr);
|
||||
frag_dbg("Started new reassembly, ID:%hu\n", ipv4_cur_frag_id);
|
||||
|
||||
if (pico_tree_insert(&ipv4_fragments, temp)) {
|
||||
frag_dbg("FRAG: Could not insert picoframe in tree\n");
|
||||
pico_frame_discard(temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pico_fragments_check_complete(&ipv4_fragments, proto, PICO_PROTO_IPV4);
|
||||
#else
|
||||
IGNORE_PARAMETER(hdr);
|
||||
IGNORE_PARAMETER(f);
|
||||
IGNORE_PARAMETER(proto);
|
||||
#endif
|
||||
}
|
||||
11
kernel/picotcp/modules/pico_fragments.h
Normal file
11
kernel/picotcp/modules/pico_fragments.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef PICO_FRAGMENTS_H
|
||||
#define PICO_FRAGMENTS_H
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_frame.h"
|
||||
|
||||
void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto);
|
||||
void pico_ipv4_process_frag(struct pico_ipv4_hdr *hdr, struct pico_frame *f, uint8_t proto);
|
||||
|
||||
#endif
|
||||
207
kernel/picotcp/modules/pico_hotplug_detection.c
Normal file
207
kernel/picotcp/modules/pico_hotplug_detection.c
Normal file
@ -0,0 +1,207 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Frederik Van Slycken
|
||||
*********************************************************************/
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_hotplug_detection.h"
|
||||
#include "pico_tree.h"
|
||||
#include "pico_device.h"
|
||||
|
||||
struct pico_hotplug_device {
|
||||
struct pico_device *dev;
|
||||
int prev_state;
|
||||
struct pico_tree callbacks;
|
||||
struct pico_tree init_callbacks; /* functions we still need to call for initialization */
|
||||
};
|
||||
|
||||
static uint32_t timer_id = 0;
|
||||
|
||||
static int pico_hotplug_dev_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_hotplug_device *a = ka, *b = kb;
|
||||
if (a->dev->hash < b->dev->hash)
|
||||
return -1;
|
||||
|
||||
if (a->dev->hash > b->dev->hash)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int callback_compare(void *ka, void *kb)
|
||||
{
|
||||
if (ka < kb)
|
||||
return -1;
|
||||
|
||||
if (ka > kb)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PICO_TREE_DECLARE(Hotplug_device_tree, pico_hotplug_dev_cmp);
|
||||
|
||||
static void initial_callbacks(struct pico_hotplug_device *hpdev, int event)
|
||||
{
|
||||
struct pico_tree_node *cb_node = NULL, *cb_safe = NULL;
|
||||
void (*cb)(struct pico_device *dev, int event);
|
||||
pico_tree_foreach_safe(cb_node, &(hpdev->init_callbacks), cb_safe)
|
||||
{
|
||||
cb = cb_node->keyValue;
|
||||
cb(hpdev->dev, event);
|
||||
pico_tree_delete(&hpdev->init_callbacks, cb);
|
||||
}
|
||||
}
|
||||
|
||||
static void execute_callbacks(struct pico_hotplug_device *hpdev, int new_state, int event)
|
||||
{
|
||||
struct pico_tree_node *cb_node = NULL, *cb_safe = NULL;
|
||||
void (*cb)(struct pico_device *dev, int event);
|
||||
if (new_state != hpdev->prev_state)
|
||||
{
|
||||
/* we don't know if one of the callbacks might deregister, so be safe */
|
||||
pico_tree_foreach_safe(cb_node, &(hpdev->callbacks), cb_safe)
|
||||
{
|
||||
cb = cb_node->keyValue;
|
||||
cb(hpdev->dev, event);
|
||||
}
|
||||
hpdev->prev_state = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_cb(__attribute__((unused)) pico_time t, __attribute__((unused)) void*v)
|
||||
{
|
||||
struct pico_tree_node *node = NULL, *safe = NULL;
|
||||
int new_state, event;
|
||||
struct pico_hotplug_device *hpdev = NULL;
|
||||
|
||||
/* we don't know if one of the callbacks might deregister, so be safe */
|
||||
pico_tree_foreach_safe(node, &Hotplug_device_tree, safe)
|
||||
{
|
||||
hpdev = node->keyValue;
|
||||
new_state = hpdev->dev->link_state(hpdev->dev);
|
||||
|
||||
if (new_state == 1) {
|
||||
event = PICO_HOTPLUG_EVENT_UP;
|
||||
} else {
|
||||
event = PICO_HOTPLUG_EVENT_DOWN;
|
||||
}
|
||||
|
||||
initial_callbacks(hpdev, event);
|
||||
execute_callbacks(hpdev, new_state, event);
|
||||
}
|
||||
|
||||
timer_id = pico_timer_add(PICO_HOTPLUG_INTERVAL, &timer_cb, NULL);
|
||||
if (timer_id == 0) {
|
||||
dbg("HOTPLUG: Failed to start timer\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int ensure_hotplug_timer(void)
|
||||
{
|
||||
if (timer_id == 0)
|
||||
{
|
||||
timer_id = pico_timer_add(PICO_HOTPLUG_INTERVAL, &timer_cb, NULL);
|
||||
if (timer_id == 0) {
|
||||
dbg("HOTPLUG: Failed to start timer\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disable_hotplug_timer(void)
|
||||
{
|
||||
if (timer_id != 0)
|
||||
{
|
||||
pico_timer_cancel(timer_id);
|
||||
timer_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int pico_hotplug_register(struct pico_device *dev, void (*cb)(struct pico_device *dev, int event))
|
||||
{
|
||||
struct pico_hotplug_device *hotplug_dev;
|
||||
struct pico_hotplug_device search = {
|
||||
.dev = dev
|
||||
};
|
||||
|
||||
/* If it does not have a link_state, */
|
||||
/* the device does not support hotplug detection */
|
||||
if (dev->link_state == NULL) {
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hotplug_dev = (struct pico_hotplug_device*)pico_tree_findKey(&Hotplug_device_tree, &search);
|
||||
if (!hotplug_dev )
|
||||
{
|
||||
hotplug_dev = PICO_ZALLOC(sizeof(struct pico_hotplug_device));
|
||||
if (!hotplug_dev)
|
||||
{
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hotplug_dev->dev = dev;
|
||||
hotplug_dev->prev_state = dev->link_state(hotplug_dev->dev);
|
||||
hotplug_dev->callbacks.root = &LEAF;
|
||||
hotplug_dev->callbacks.compare = &callback_compare;
|
||||
hotplug_dev->init_callbacks.root = &LEAF;
|
||||
hotplug_dev->init_callbacks.compare = &callback_compare;
|
||||
if (pico_tree_insert(&Hotplug_device_tree, hotplug_dev)) {
|
||||
PICO_FREE(hotplug_dev);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pico_tree_insert(&(hotplug_dev->callbacks), cb) == &LEAF) {
|
||||
PICO_FREE(hotplug_dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_tree_insert(&(hotplug_dev->init_callbacks), cb) == &LEAF) {
|
||||
pico_tree_delete(&(hotplug_dev->callbacks), cb);
|
||||
PICO_FREE(hotplug_dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ensure_hotplug_timer() < 0) {
|
||||
pico_hotplug_deregister((struct pico_device *)hotplug_dev, cb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_hotplug_deregister(struct pico_device *dev, void (*cb)(struct pico_device *dev, int event))
|
||||
{
|
||||
struct pico_hotplug_device*hotplug_dev;
|
||||
struct pico_hotplug_device search = {
|
||||
.dev = dev
|
||||
};
|
||||
|
||||
hotplug_dev = (struct pico_hotplug_device*)pico_tree_findKey(&Hotplug_device_tree, &search);
|
||||
if (!hotplug_dev)
|
||||
/* wasn't registered */
|
||||
return 0;
|
||||
|
||||
pico_tree_delete(&hotplug_dev->callbacks, cb);
|
||||
pico_tree_delete(&hotplug_dev->init_callbacks, cb);
|
||||
if (pico_tree_empty(&hotplug_dev->callbacks))
|
||||
{
|
||||
pico_tree_delete(&Hotplug_device_tree, hotplug_dev);
|
||||
PICO_FREE(hotplug_dev);
|
||||
}
|
||||
|
||||
if (pico_tree_empty(&Hotplug_device_tree))
|
||||
{
|
||||
disable_hotplug_timer();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
23
kernel/picotcp/modules/pico_hotplug_detection.h
Normal file
23
kernel/picotcp/modules/pico_hotplug_detection.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Frederik Van Slycken
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_SUPPORT_HOTPLUG
|
||||
#define INCLUDE_PICO_SUPPORT_HOTPLUG
|
||||
#include "pico_stack.h"
|
||||
|
||||
#define PICO_HOTPLUG_EVENT_UP 1 /* link went up */
|
||||
#define PICO_HOTPLUG_EVENT_DOWN 2 /* link went down */
|
||||
|
||||
#define PICO_HOTPLUG_INTERVAL 100
|
||||
|
||||
/* register your callback to be notified of hotplug events on a certain device.
|
||||
* Note that each callback will be called at least once, shortly after adding, for initialization.
|
||||
*/
|
||||
int pico_hotplug_register(struct pico_device *dev, void (*cb)(struct pico_device *dev, int event));
|
||||
int pico_hotplug_deregister(struct pico_device *dev, void (*cb)(struct pico_device *dev, int event));
|
||||
|
||||
#endif /* _INCLUDE_PICO_SUPPORT_HOTPLUG */
|
||||
|
||||
434
kernel/picotcp/modules/pico_icmp4.c
Normal file
434
kernel/picotcp/modules/pico_icmp4.c
Normal file
@ -0,0 +1,434 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_icmp4.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_eth.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_tree.h"
|
||||
|
||||
/* Queues */
|
||||
static struct pico_queue icmp_in = {
|
||||
0
|
||||
};
|
||||
static struct pico_queue icmp_out = {
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
/* Functions */
|
||||
|
||||
static int pico_icmp4_checksum(struct pico_frame *f)
|
||||
{
|
||||
struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
|
||||
if (!hdr) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr->crc = 0;
|
||||
hdr->crc = short_be(pico_checksum(hdr, f->transport_len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_PING
|
||||
static void ping_recv_reply(struct pico_frame *f);
|
||||
#endif
|
||||
|
||||
static int pico_icmp4_process_in(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
|
||||
static int firstpkt = 1;
|
||||
static uint16_t last_id = 0;
|
||||
static uint16_t last_seq = 0;
|
||||
IGNORE_PARAMETER(self);
|
||||
|
||||
if (hdr->type == PICO_ICMP_ECHO) {
|
||||
hdr->type = PICO_ICMP_ECHOREPLY;
|
||||
/* outgoing frames require a f->len without the ethernet header len */
|
||||
if (f->dev && f->dev->eth)
|
||||
f->len -= PICO_SIZE_ETHHDR;
|
||||
|
||||
if (!firstpkt && (hdr->hun.ih_idseq.idseq_id == last_id) && (last_seq == hdr->hun.ih_idseq.idseq_seq)) {
|
||||
/* The network duplicated the echo. Do not reply. */
|
||||
pico_frame_discard(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
firstpkt = 0;
|
||||
last_id = hdr->hun.ih_idseq.idseq_id;
|
||||
last_seq = hdr->hun.ih_idseq.idseq_seq;
|
||||
pico_icmp4_checksum(f);
|
||||
pico_ipv4_rebound(f);
|
||||
} else if (hdr->type == PICO_ICMP_UNREACH) {
|
||||
f->net_hdr = f->transport_hdr + PICO_ICMPHDR_UN_SIZE;
|
||||
pico_ipv4_unreachable(f, hdr->code);
|
||||
} else if (hdr->type == PICO_ICMP_ECHOREPLY) {
|
||||
#ifdef PICO_SUPPORT_PING
|
||||
ping_recv_reply(f);
|
||||
#endif
|
||||
pico_frame_discard(f);
|
||||
} else {
|
||||
pico_frame_discard(f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_icmp4_process_out(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(self);
|
||||
IGNORE_PARAMETER(f);
|
||||
dbg("Called %s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Interface: protocol definition */
|
||||
struct pico_protocol pico_proto_icmp4 = {
|
||||
.name = "icmp4",
|
||||
.proto_number = PICO_PROTO_ICMP4,
|
||||
.layer = PICO_LAYER_TRANSPORT,
|
||||
.process_in = pico_icmp4_process_in,
|
||||
.process_out = pico_icmp4_process_out,
|
||||
.q_in = &icmp_in,
|
||||
.q_out = &icmp_out,
|
||||
};
|
||||
|
||||
static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code)
|
||||
{
|
||||
struct pico_frame *reply;
|
||||
struct pico_icmp4_hdr *hdr;
|
||||
struct pico_ipv4_hdr *info;
|
||||
uint16_t f_tot_len;
|
||||
|
||||
if (f == NULL) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
f_tot_len = short_be(((struct pico_ipv4_hdr *)f->net_hdr)->len);
|
||||
|
||||
if (f_tot_len < (sizeof(struct pico_ipv4_hdr)))
|
||||
return -1;
|
||||
|
||||
/* Truncate tot len to be at most 8 bytes + iphdr */
|
||||
if (f_tot_len > (sizeof(struct pico_ipv4_hdr) + 8u)) {
|
||||
f_tot_len = (sizeof(struct pico_ipv4_hdr) + 8u);
|
||||
}
|
||||
|
||||
reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, f->dev, (uint16_t) (f_tot_len + PICO_ICMPHDR_UN_SIZE));
|
||||
info = (struct pico_ipv4_hdr*)(f->net_hdr);
|
||||
hdr = (struct pico_icmp4_hdr *) reply->transport_hdr;
|
||||
hdr->type = type;
|
||||
hdr->code = code;
|
||||
hdr->hun.ih_pmtu.ipm_nmtu = short_be(1500);
|
||||
hdr->hun.ih_pmtu.ipm_void = 0;
|
||||
reply->transport_len = (uint16_t)(f_tot_len + PICO_ICMPHDR_UN_SIZE);
|
||||
reply->payload = reply->transport_hdr + PICO_ICMPHDR_UN_SIZE;
|
||||
memcpy(reply->payload, f->net_hdr, f_tot_len);
|
||||
pico_icmp4_checksum(reply);
|
||||
pico_ipv4_frame_push(reply, &info->src, PICO_PROTO_ICMP4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_icmp4_port_unreachable(struct pico_frame *f)
|
||||
{
|
||||
/*Parameter check executed in pico_icmp4_notify*/
|
||||
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PORT);
|
||||
}
|
||||
|
||||
int pico_icmp4_proto_unreachable(struct pico_frame *f)
|
||||
{
|
||||
/*Parameter check executed in pico_icmp4_notify*/
|
||||
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PROTOCOL);
|
||||
}
|
||||
|
||||
int pico_icmp4_dest_unreachable(struct pico_frame *f)
|
||||
{
|
||||
/*Parameter check executed in pico_icmp4_notify*/
|
||||
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_HOST);
|
||||
}
|
||||
|
||||
int pico_icmp4_ttl_expired(struct pico_frame *f)
|
||||
{
|
||||
/*Parameter check executed in pico_icmp4_notify*/
|
||||
return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_INTRANS);
|
||||
}
|
||||
|
||||
MOCKABLE int pico_icmp4_frag_expired(struct pico_frame *f)
|
||||
{
|
||||
/*Parameter check executed in pico_icmp4_notify*/
|
||||
return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_REASS);
|
||||
}
|
||||
|
||||
int pico_icmp4_mtu_exceeded(struct pico_frame *f)
|
||||
{
|
||||
/*Parameter check executed in pico_icmp4_notify*/
|
||||
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_NEEDFRAG);
|
||||
}
|
||||
|
||||
int pico_icmp4_packet_filtered(struct pico_frame *f)
|
||||
{
|
||||
/*Parameter check executed in pico_icmp4_notify*/
|
||||
/*Packet Filtered: type 3, code 13 (Communication Administratively Prohibited)*/
|
||||
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_FILTER_PROHIB);
|
||||
}
|
||||
|
||||
int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code)
|
||||
{
|
||||
return pico_icmp4_notify(f, PICO_ICMP_PARAMPROB, code);
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* Ping implementation */
|
||||
/***********************/
|
||||
/***********************/
|
||||
/***********************/
|
||||
/***********************/
|
||||
|
||||
|
||||
#ifdef PICO_SUPPORT_PING
|
||||
|
||||
|
||||
struct pico_icmp4_ping_cookie
|
||||
{
|
||||
struct pico_ip4 dst;
|
||||
uint16_t err;
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
uint16_t size;
|
||||
int count;
|
||||
pico_time timestamp;
|
||||
int interval;
|
||||
int timeout;
|
||||
void (*cb)(struct pico_icmp4_stats*);
|
||||
};
|
||||
|
||||
static int cookie_compare(void *ka, void *kb)
|
||||
{
|
||||
struct pico_icmp4_ping_cookie *a = ka, *b = kb;
|
||||
if (a->id < b->id)
|
||||
return -1;
|
||||
|
||||
if (a->id > b->id)
|
||||
return 1;
|
||||
|
||||
return (a->seq - b->seq);
|
||||
}
|
||||
|
||||
static PICO_TREE_DECLARE(Pings, cookie_compare);
|
||||
|
||||
static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
|
||||
{
|
||||
struct pico_frame *echo = NULL;
|
||||
struct pico_icmp4_hdr *hdr;
|
||||
struct pico_device *dev = pico_ipv4_source_dev_find(&cookie->dst);
|
||||
if (!dev)
|
||||
return -1;
|
||||
|
||||
echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size));
|
||||
if (!echo)
|
||||
return -1;
|
||||
|
||||
hdr = (struct pico_icmp4_hdr *) echo->transport_hdr;
|
||||
|
||||
hdr->type = PICO_ICMP_ECHO;
|
||||
hdr->code = 0;
|
||||
hdr->hun.ih_idseq.idseq_id = short_be(cookie->id);
|
||||
hdr->hun.ih_idseq.idseq_seq = short_be(cookie->seq);
|
||||
echo->transport_len = (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size);
|
||||
echo->payload = echo->transport_hdr + PICO_ICMPHDR_UN_SIZE;
|
||||
echo->payload_len = cookie->size;
|
||||
/* XXX: Fill payload */
|
||||
pico_icmp4_checksum(echo);
|
||||
pico_ipv4_frame_push(echo, &cookie->dst, PICO_PROTO_ICMP4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ping_timeout(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg;
|
||||
IGNORE_PARAMETER(now);
|
||||
|
||||
if(pico_tree_findKey(&Pings, cookie)) {
|
||||
if (cookie->err == PICO_PING_ERR_PENDING) {
|
||||
struct pico_icmp4_stats stats;
|
||||
stats.dst = cookie->dst;
|
||||
stats.seq = cookie->seq;
|
||||
stats.time = 0;
|
||||
stats.size = cookie->size;
|
||||
stats.err = PICO_PING_ERR_TIMEOUT;
|
||||
dbg(" ---- Ping timeout!!!\n");
|
||||
cookie->cb(&stats);
|
||||
}
|
||||
|
||||
pico_tree_delete(&Pings, cookie);
|
||||
PICO_FREE(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
static void next_ping(pico_time now, void *arg);
|
||||
static int send_ping(struct pico_icmp4_ping_cookie *cookie)
|
||||
{
|
||||
uint32_t timeout_timer = 0;
|
||||
struct pico_icmp4_stats stats;
|
||||
pico_icmp4_send_echo(cookie);
|
||||
cookie->timestamp = pico_tick;
|
||||
timeout_timer = pico_timer_add((uint32_t)cookie->timeout, ping_timeout, cookie);
|
||||
if (!timeout_timer) {
|
||||
goto fail;
|
||||
}
|
||||
if (cookie->seq < (uint16_t)cookie->count) {
|
||||
if (!pico_timer_add((uint32_t)cookie->interval, next_ping, cookie)) {
|
||||
pico_timer_cancel(timeout_timer);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
dbg("ICMP4: Failed to start timer\n");
|
||||
cookie->err = PICO_PING_ERR_ABORTED;
|
||||
stats.err = cookie->err;
|
||||
cookie->cb(&stats);
|
||||
pico_tree_delete(&Pings, cookie);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void next_ping(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg;
|
||||
IGNORE_PARAMETER(now);
|
||||
|
||||
if(pico_tree_findKey(&Pings, cookie)) {
|
||||
if (cookie->err == PICO_PING_ERR_ABORTED)
|
||||
return;
|
||||
|
||||
if (cookie->seq < (uint16_t)cookie->count) {
|
||||
newcookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie));
|
||||
if (!newcookie)
|
||||
return;
|
||||
|
||||
memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie));
|
||||
newcookie->seq++;
|
||||
|
||||
if (pico_tree_insert(&Pings, newcookie)) {
|
||||
dbg("ICMP4: Failed to insert new cookie in tree \n");
|
||||
PICO_FREE(newcookie);
|
||||
return;
|
||||
}
|
||||
|
||||
if (send_ping(newcookie)) {
|
||||
dbg("ICMP4: Failed to send ping\n");
|
||||
PICO_FREE(newcookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ping_recv_reply(struct pico_frame *f)
|
||||
{
|
||||
struct pico_icmp4_ping_cookie test, *cookie;
|
||||
struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
|
||||
test.id = short_be(hdr->hun.ih_idseq.idseq_id );
|
||||
test.seq = short_be(hdr->hun.ih_idseq.idseq_seq);
|
||||
|
||||
cookie = pico_tree_findKey(&Pings, &test);
|
||||
if (cookie) {
|
||||
struct pico_icmp4_stats stats;
|
||||
if (cookie->err == PICO_PING_ERR_ABORTED)
|
||||
return;
|
||||
|
||||
cookie->err = PICO_PING_ERR_REPLIED;
|
||||
stats.dst = ((struct pico_ipv4_hdr *)f->net_hdr)->src;
|
||||
stats.seq = cookie->seq;
|
||||
stats.size = cookie->size;
|
||||
stats.time = pico_tick - cookie->timestamp;
|
||||
stats.err = cookie->err;
|
||||
stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl;
|
||||
if(cookie->cb != NULL)
|
||||
cookie->cb(&stats);
|
||||
} else {
|
||||
dbg("Reply for seq=%d, not found.\n", test.seq);
|
||||
}
|
||||
}
|
||||
|
||||
int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *))
|
||||
{
|
||||
static uint16_t next_id = 0x91c0;
|
||||
struct pico_icmp4_ping_cookie *cookie;
|
||||
|
||||
if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie));
|
||||
if (!cookie) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_string_to_ipv4(dst, (uint32_t *)&cookie->dst.addr) < 0) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
PICO_FREE(cookie);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cookie->seq = 1;
|
||||
cookie->id = next_id++;
|
||||
cookie->err = PICO_PING_ERR_PENDING;
|
||||
cookie->size = (uint16_t)size;
|
||||
cookie->interval = interval;
|
||||
cookie->timeout = timeout;
|
||||
cookie->cb = cb;
|
||||
cookie->count = count;
|
||||
|
||||
if (pico_tree_insert(&Pings, cookie)) {
|
||||
dbg("ICMP4: Failed to insert cookie in tree \n");
|
||||
PICO_FREE(cookie);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (send_ping(cookie)) {
|
||||
PICO_FREE(cookie);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cookie->id;
|
||||
|
||||
}
|
||||
|
||||
int pico_icmp4_ping_abort(int id)
|
||||
{
|
||||
struct pico_tree_node *node;
|
||||
int found = 0;
|
||||
pico_tree_foreach(node, &Pings)
|
||||
{
|
||||
struct pico_icmp4_ping_cookie *ck =
|
||||
(struct pico_icmp4_ping_cookie *) node->keyValue;
|
||||
if (ck->id == (uint16_t)id) {
|
||||
ck->err = PICO_PING_ERR_ABORTED;
|
||||
found++;
|
||||
}
|
||||
}
|
||||
if (found > 0)
|
||||
return 0; /* OK if at least one pending ping has been canceled */
|
||||
|
||||
pico_err = PICO_ERR_ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
162
kernel/picotcp/modules/pico_icmp4.h
Normal file
162
kernel/picotcp/modules/pico_icmp4.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_ICMP4
|
||||
#define INCLUDE_PICO_ICMP4
|
||||
#include "pico_defines.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
|
||||
|
||||
extern struct pico_protocol pico_proto_icmp4;
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp4_hdr {
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t crc;
|
||||
|
||||
/* hun */
|
||||
PACKED_UNION_DEF hun_u {
|
||||
uint8_t ih_pptr;
|
||||
struct pico_ip4 ih_gwaddr;
|
||||
PEDANTIC_STRUCT_DEF ih_idseq_s {
|
||||
uint16_t idseq_id;
|
||||
uint16_t idseq_seq;
|
||||
} ih_idseq;
|
||||
uint32_t ih_void;
|
||||
PEDANTIC_STRUCT_DEF ih_pmtu_s {
|
||||
uint16_t ipm_void;
|
||||
uint16_t ipm_nmtu;
|
||||
} ih_pmtu;
|
||||
PEDANTIC_STRUCT_DEF ih_rta_s {
|
||||
uint8_t rta_numgw;
|
||||
uint8_t rta_wpa;
|
||||
uint16_t rta_lifetime;
|
||||
} ih_rta;
|
||||
} hun;
|
||||
|
||||
/* dun */
|
||||
PACKED_UNION_DEF dun_u {
|
||||
PEDANTIC_STRUCT_DEF id_ts_s {
|
||||
uint32_t ts_otime;
|
||||
uint32_t ts_rtime;
|
||||
uint32_t ts_ttime;
|
||||
} id_ts;
|
||||
PEDANTIC_STRUCT_DEF id_ip_s {
|
||||
uint32_t ip_options;
|
||||
uint32_t ip_data_hi;
|
||||
uint32_t ip_data_lo;
|
||||
} id_ip;
|
||||
PEDANTIC_STRUCT_DEF id_ra_s {
|
||||
uint32_t ira_addr;
|
||||
uint32_t ira_pref;
|
||||
} id_ra;
|
||||
uint32_t id_mask;
|
||||
uint8_t id_data[1];
|
||||
} dun;
|
||||
};
|
||||
|
||||
#define PICO_ICMPHDR_DRY_SIZE 4
|
||||
#define PICO_ICMPHDR_UN_SIZE 8u
|
||||
|
||||
#define PICO_ICMP_ECHOREPLY 0
|
||||
#define PICO_ICMP_DEST_UNREACH 3
|
||||
#define PICO_ICMP_SOURCE_QUENCH 4
|
||||
#define PICO_ICMP_REDIRECT 5
|
||||
#define PICO_ICMP_ECHO 8
|
||||
#define PICO_ICMP_TIME_EXCEEDED 11
|
||||
#define PICO_ICMP_PARAMETERPROB 12
|
||||
#define PICO_ICMP_TIMESTAMP 13
|
||||
#define PICO_ICMP_TIMESTAMPREPLY 14
|
||||
#define PICO_ICMP_INFO_REQUEST 15
|
||||
#define PICO_ICMP_INFO_REPLY 16
|
||||
#define PICO_ICMP_ADDRESS 17
|
||||
#define PICO_ICMP_ADDRESSREPLY 18
|
||||
|
||||
|
||||
#define PICO_ICMP_UNREACH 3
|
||||
#define PICO_ICMP_SOURCEQUENCH 4
|
||||
#define PICO_ICMP_ROUTERADVERT 9
|
||||
#define PICO_ICMP_ROUTERSOLICIT 10
|
||||
#define PICO_ICMP_TIMXCEED 11
|
||||
#define PICO_ICMP_PARAMPROB 12
|
||||
#define PICO_ICMP_TSTAMP 13
|
||||
#define PICO_ICMP_TSTAMPREPLY 14
|
||||
#define PICO_ICMP_IREQ 15
|
||||
#define PICO_ICMP_IREQREPLY 16
|
||||
#define PICO_ICMP_MASKREQ 17
|
||||
#define PICO_ICMP_MASKREPLY 18
|
||||
|
||||
#define PICO_ICMP_MAXTYPE 18
|
||||
|
||||
|
||||
#define PICO_ICMP_UNREACH_NET 0
|
||||
#define PICO_ICMP_UNREACH_HOST 1
|
||||
#define PICO_ICMP_UNREACH_PROTOCOL 2
|
||||
#define PICO_ICMP_UNREACH_PORT 3
|
||||
#define PICO_ICMP_UNREACH_NEEDFRAG 4
|
||||
#define PICO_ICMP_UNREACH_SRCFAIL 5
|
||||
#define PICO_ICMP_UNREACH_NET_UNKNOWN 6
|
||||
#define PICO_ICMP_UNREACH_HOST_UNKNOWN 7
|
||||
#define PICO_ICMP_UNREACH_ISOLATED 8
|
||||
#define PICO_ICMP_UNREACH_NET_PROHIB 9
|
||||
#define PICO_ICMP_UNREACH_HOST_PROHIB 10
|
||||
#define PICO_ICMP_UNREACH_TOSNET 11
|
||||
#define PICO_ICMP_UNREACH_TOSHOST 12
|
||||
#define PICO_ICMP_UNREACH_FILTER_PROHIB 13
|
||||
#define PICO_ICMP_UNREACH_HOST_PRECEDENCE 14
|
||||
#define PICO_ICMP_UNREACH_PRECEDENCE_CUTOFF 15
|
||||
|
||||
|
||||
#define PICO_ICMP_REDIRECT_NET 0
|
||||
#define PICO_ICMP_REDIRECT_HOST 1
|
||||
#define PICO_ICMP_REDIRECT_TOSNET 2
|
||||
#define PICO_ICMP_REDIRECT_TOSHOST 3
|
||||
|
||||
|
||||
#define PICO_ICMP_TIMXCEED_INTRANS 0
|
||||
#define PICO_ICMP_TIMXCEED_REASS 1
|
||||
|
||||
|
||||
#define PICO_ICMP_PARAMPROB_OPTABSENT 1
|
||||
|
||||
#define PICO_SIZE_ICMP4HDR ((sizeof(struct pico_icmp4_hdr)))
|
||||
|
||||
struct pico_icmp4_stats
|
||||
{
|
||||
struct pico_ip4 dst;
|
||||
unsigned long size;
|
||||
unsigned long seq;
|
||||
pico_time time;
|
||||
unsigned long ttl;
|
||||
int err;
|
||||
};
|
||||
|
||||
int pico_icmp4_port_unreachable(struct pico_frame *f);
|
||||
int pico_icmp4_proto_unreachable(struct pico_frame *f);
|
||||
int pico_icmp4_dest_unreachable(struct pico_frame *f);
|
||||
int pico_icmp4_mtu_exceeded(struct pico_frame *f);
|
||||
int pico_icmp4_ttl_expired(struct pico_frame *f);
|
||||
int pico_icmp4_frag_expired(struct pico_frame *f);
|
||||
int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *));
|
||||
int pico_icmp4_ping_abort(int id);
|
||||
|
||||
#ifdef PICO_SUPPORT_ICMP4
|
||||
int pico_icmp4_packet_filtered(struct pico_frame *f);
|
||||
int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code);
|
||||
#else
|
||||
# define pico_icmp4_packet_filtered(f) (-1)
|
||||
# define pico_icmp4_param_problem(f, c) (-1)
|
||||
#endif /* PICO_SUPPORT_ICMP4 */
|
||||
|
||||
#define PICO_PING_ERR_REPLIED 0
|
||||
#define PICO_PING_ERR_TIMEOUT 1
|
||||
#define PICO_PING_ERR_UNREACH 2
|
||||
#define PICO_PING_ERR_ABORTED 3
|
||||
#define PICO_PING_ERR_PENDING 0xFFFF
|
||||
|
||||
#endif
|
||||
901
kernel/picotcp/modules/pico_icmp6.c
Normal file
901
kernel/picotcp/modules/pico_icmp6.c
Normal file
@ -0,0 +1,901 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Kristof Roelants, Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_icmp6.h"
|
||||
#include "pico_ipv6_nd.h"
|
||||
#include "pico_6lowpan.h"
|
||||
#include "pico_eth.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_tree.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_mld.h"
|
||||
|
||||
#ifdef DEBUG_ICMP6
|
||||
#define icmp6_dbg dbg
|
||||
#else
|
||||
#define icmp6_dbg(...) do { } while(0)
|
||||
#endif
|
||||
|
||||
static struct pico_queue icmp6_in;
|
||||
static struct pico_queue icmp6_out;
|
||||
|
||||
/******************************************************************************
|
||||
* Function prototypes
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
static int pico_6lp_nd_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst);
|
||||
#endif
|
||||
|
||||
uint16_t pico_icmp6_checksum(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
|
||||
struct pico_icmp6_hdr *icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
|
||||
struct pico_ipv6_pseudo_hdr pseudo;
|
||||
|
||||
pseudo.src = ipv6_hdr->src;
|
||||
pseudo.dst = ipv6_hdr->dst;
|
||||
pseudo.len = long_be(f->transport_len);
|
||||
pseudo.nxthdr = PICO_PROTO_ICMP6;
|
||||
|
||||
pseudo.zero[0] = 0;
|
||||
pseudo.zero[1] = 0;
|
||||
pseudo.zero[2] = 0;
|
||||
|
||||
return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv6_pseudo_hdr), icmp6_hdr, f->transport_len);
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_PING
|
||||
static void pico_icmp6_ping_recv_reply(struct pico_frame *f);
|
||||
#endif
|
||||
|
||||
static int pico_icmp6_send_echoreply(struct pico_frame *echo)
|
||||
{
|
||||
struct pico_frame *reply = NULL;
|
||||
struct pico_icmp6_hdr *ehdr = NULL, *rhdr = NULL;
|
||||
struct pico_ip6 src;
|
||||
struct pico_ip6 dst;
|
||||
|
||||
reply = pico_proto_ipv6.alloc(&pico_proto_ipv6, echo->dev, (uint16_t)(echo->transport_len));
|
||||
if (!reply) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
echo->payload = echo->transport_hdr + PICO_ICMP6HDR_ECHO_REQUEST_SIZE;
|
||||
reply->payload = reply->transport_hdr + PICO_ICMP6HDR_ECHO_REQUEST_SIZE;
|
||||
reply->payload_len = echo->transport_len;
|
||||
|
||||
ehdr = (struct pico_icmp6_hdr *)echo->transport_hdr;
|
||||
rhdr = (struct pico_icmp6_hdr *)reply->transport_hdr;
|
||||
rhdr->type = PICO_ICMP6_ECHO_REPLY;
|
||||
rhdr->code = 0;
|
||||
rhdr->msg.info.echo_reply.id = ehdr->msg.info.echo_reply.id;
|
||||
rhdr->msg.info.echo_reply.seq = ehdr->msg.info.echo_request.seq;
|
||||
memcpy(reply->payload, echo->payload, (uint32_t)(echo->transport_len - PICO_ICMP6HDR_ECHO_REQUEST_SIZE));
|
||||
rhdr->crc = 0;
|
||||
rhdr->crc = short_be(pico_icmp6_checksum(reply));
|
||||
/* Get destination and source swapped */
|
||||
memcpy(dst.addr, ((struct pico_ipv6_hdr *)echo->net_hdr)->src.addr, PICO_SIZE_IP6);
|
||||
memcpy(src.addr, ((struct pico_ipv6_hdr *)echo->net_hdr)->dst.addr, PICO_SIZE_IP6);
|
||||
pico_ipv6_frame_push(reply, &src, &dst, PICO_PROTO_ICMP6, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pico_icmp6_process_in(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
struct pico_icmp6_hdr *hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
|
||||
|
||||
IGNORE_PARAMETER(self);
|
||||
|
||||
icmp6_dbg("Process IN, type = %d\n", hdr->type);
|
||||
|
||||
switch (hdr->type)
|
||||
{
|
||||
case PICO_ICMP6_DEST_UNREACH:
|
||||
pico_ipv6_unreachable(f, hdr->code);
|
||||
break;
|
||||
|
||||
case PICO_ICMP6_ECHO_REQUEST:
|
||||
icmp6_dbg("ICMP6: Received ECHO REQ\n");
|
||||
f->transport_len = (uint16_t)(f->len - f->net_len - (uint16_t)(f->net_hdr - f->buffer));
|
||||
pico_icmp6_send_echoreply(f);
|
||||
pico_frame_discard(f);
|
||||
break;
|
||||
|
||||
case PICO_ICMP6_ECHO_REPLY:
|
||||
#ifdef PICO_SUPPORT_PING
|
||||
pico_icmp6_ping_recv_reply(f);
|
||||
#endif
|
||||
pico_frame_discard(f);
|
||||
break;
|
||||
#if defined(PICO_SUPPORT_MCAST) && defined(PICO_SUPPORT_MLD)
|
||||
case PICO_MLD_QUERY:
|
||||
case PICO_MLD_REPORT:
|
||||
case PICO_MLD_DONE:
|
||||
case PICO_MLD_REPORTV2:
|
||||
pico_mld_process_in(f);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return pico_ipv6_nd_recv(f); /* CAUTION -- Implies: pico_frame_discard in any case, keep in the default! */
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pico_icmp6_process_out(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(self);
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Interface: protocol definition */
|
||||
struct pico_protocol pico_proto_icmp6 = {
|
||||
.name = "icmp6",
|
||||
.proto_number = PICO_PROTO_ICMP6,
|
||||
.layer = PICO_LAYER_TRANSPORT,
|
||||
.process_in = pico_icmp6_process_in,
|
||||
.process_out = pico_icmp6_process_out,
|
||||
.q_in = &icmp6_in,
|
||||
.q_out = &icmp6_out,
|
||||
};
|
||||
|
||||
static int pico_icmp6_notify(struct pico_frame *f, uint8_t type, uint8_t code, uint32_t ptr)
|
||||
{
|
||||
struct pico_frame *notice = NULL;
|
||||
struct pico_ipv6_hdr *ipv6_hdr = NULL;
|
||||
struct pico_icmp6_hdr *icmp6_hdr = NULL;
|
||||
uint16_t len = 0;
|
||||
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
ipv6_hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
|
||||
len = (uint16_t)(short_be(ipv6_hdr->len) + PICO_SIZE_IP6HDR);
|
||||
switch (type)
|
||||
{
|
||||
case PICO_ICMP6_DEST_UNREACH:
|
||||
/* as much of invoking packet as possible without exceeding the minimum IPv6 MTU */
|
||||
if (PICO_SIZE_IP6HDR + PICO_ICMP6HDR_DEST_UNREACH_SIZE + len > PICO_IPV6_MIN_MTU)
|
||||
len = PICO_IPV6_MIN_MTU - (PICO_SIZE_IP6HDR + PICO_ICMP6HDR_DEST_UNREACH_SIZE);
|
||||
|
||||
notice = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, (uint16_t)(PICO_ICMP6HDR_DEST_UNREACH_SIZE + len));
|
||||
if (!notice) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
notice->payload = notice->transport_hdr + PICO_ICMP6HDR_DEST_UNREACH_SIZE;
|
||||
notice->payload_len = len;
|
||||
icmp6_hdr = (struct pico_icmp6_hdr *)notice->transport_hdr;
|
||||
icmp6_hdr->msg.err.dest_unreach.unused = 0;
|
||||
break;
|
||||
|
||||
case PICO_ICMP6_TIME_EXCEEDED:
|
||||
/* as much of invoking packet as possible without exceeding the minimum IPv6 MTU */
|
||||
if (PICO_SIZE_IP6HDR + PICO_ICMP6HDR_TIME_XCEEDED_SIZE + len > PICO_IPV6_MIN_MTU)
|
||||
len = PICO_IPV6_MIN_MTU - (PICO_SIZE_IP6HDR + PICO_ICMP6HDR_TIME_XCEEDED_SIZE);
|
||||
|
||||
notice = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, (uint16_t)(PICO_ICMP6HDR_TIME_XCEEDED_SIZE + len));
|
||||
if (!notice) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
notice->payload = notice->transport_hdr + PICO_ICMP6HDR_TIME_XCEEDED_SIZE;
|
||||
notice->payload_len = len;
|
||||
icmp6_hdr = (struct pico_icmp6_hdr *)notice->transport_hdr;
|
||||
icmp6_hdr->msg.err.time_exceeded.unused = 0;
|
||||
break;
|
||||
|
||||
case PICO_ICMP6_PARAM_PROBLEM:
|
||||
if (PICO_SIZE_IP6HDR + PICO_ICMP6HDR_PARAM_PROBLEM_SIZE + len > PICO_IPV6_MIN_MTU)
|
||||
len = PICO_IPV6_MIN_MTU - (PICO_SIZE_IP6HDR + PICO_ICMP6HDR_PARAM_PROBLEM_SIZE);
|
||||
|
||||
notice = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, (uint16_t)(PICO_ICMP6HDR_PARAM_PROBLEM_SIZE + len));
|
||||
if (!notice) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
notice->payload = notice->transport_hdr + PICO_ICMP6HDR_PARAM_PROBLEM_SIZE;
|
||||
notice->payload_len = len;
|
||||
icmp6_hdr = (struct pico_icmp6_hdr *)notice->transport_hdr;
|
||||
icmp6_hdr->msg.err.param_problem.ptr = long_be(ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
icmp6_hdr->type = type;
|
||||
icmp6_hdr->code = code;
|
||||
memcpy(notice->payload, f->net_hdr, notice->payload_len);
|
||||
/* f->src is set in frame_push, checksum calculated there */
|
||||
pico_ipv6_frame_push(notice, NULL, &ipv6_hdr->src, PICO_PROTO_ICMP6, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_icmp6_port_unreachable(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
if (pico_ipv6_is_multicast(hdr->dst.addr))
|
||||
return 0;
|
||||
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_DEST_UNREACH, PICO_ICMP6_UNREACH_PORT, 0);
|
||||
}
|
||||
|
||||
int pico_icmp6_proto_unreachable(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
if (pico_ipv6_is_multicast(hdr->dst.addr))
|
||||
return 0;
|
||||
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_DEST_UNREACH, PICO_ICMP6_UNREACH_ADDR, 0);
|
||||
}
|
||||
|
||||
int pico_icmp6_dest_unreachable(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
if (pico_ipv6_is_multicast(hdr->dst.addr))
|
||||
return 0;
|
||||
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_DEST_UNREACH, PICO_ICMP6_UNREACH_ADDR, 0);
|
||||
}
|
||||
|
||||
int pico_icmp6_ttl_expired(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
if (pico_ipv6_is_multicast(hdr->dst.addr))
|
||||
return 0;
|
||||
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_TIME_EXCEEDED, PICO_ICMP6_TIMXCEED_INTRANS, 0);
|
||||
}
|
||||
|
||||
int pico_icmp6_pkt_too_big(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
if (pico_ipv6_is_multicast(hdr->dst.addr))
|
||||
return 0;
|
||||
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_PKT_TOO_BIG, 0, 0);
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_IPFILTER
|
||||
int pico_icmp6_packet_filtered(struct pico_frame *f)
|
||||
{
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_DEST_UNREACH, PICO_ICMP6_UNREACH_ADMIN, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
int pico_icmp6_parameter_problem(struct pico_frame *f, uint8_t problem, uint32_t ptr)
|
||||
{
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_PARAM_PROBLEM, problem, ptr);
|
||||
}
|
||||
|
||||
MOCKABLE int pico_icmp6_frag_expired(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
if (pico_ipv6_is_multicast(hdr->dst.addr))
|
||||
return 0;
|
||||
|
||||
return pico_icmp6_notify(f, PICO_ICMP6_TIME_EXCEEDED, PICO_ICMP6_TIMXCEED_REASS, 0);
|
||||
}
|
||||
|
||||
/* Provide a Link-Layer Address Option, either Source (SLLAO) or Destination (DLLAO) */
|
||||
static int pico_icmp6_provide_llao(struct pico_icmp6_opt_lladdr *llao, uint8_t type, struct pico_device *dev, struct pico_ip6 *src)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
struct pico_6lowpan_info *info = (struct pico_6lowpan_info *)dev->eth;
|
||||
#endif
|
||||
IGNORE_PARAMETER(src);
|
||||
llao->type = type;
|
||||
|
||||
if (!dev->mode && dev->eth) {
|
||||
memcpy(llao->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
llao->len = 1;
|
||||
}
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
else if (PICO_DEV_IS_6LOWPAN(dev) && dev->eth) {
|
||||
if (src && IID_16(&src->addr[8])) {
|
||||
memcpy(llao->addr.pan.data, (uint8_t *)&info->addr_short.addr, SIZE_6LOWPAN_SHORT);
|
||||
memset(llao->addr.pan.data + SIZE_6LOWPAN_SHORT, 0, 4);
|
||||
llao->len = 1;
|
||||
} else {
|
||||
memcpy(llao->addr.pan.data, info->addr_ext.addr, SIZE_6LOWPAN_EXT);
|
||||
memset(llao->addr.pan.data + SIZE_6LOWPAN_EXT, 0, 6);
|
||||
llao->len = 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepares a ICMP6 neighbor solicitation message */
|
||||
static struct pico_frame *pico_icmp6_neigh_sol_prep(struct pico_device *dev, struct pico_ip6 *dst, uint16_t len)
|
||||
{
|
||||
struct pico_icmp6_hdr *icmp = NULL;
|
||||
struct pico_frame *sol = NULL;
|
||||
IGNORE_PARAMETER(dev);
|
||||
|
||||
/* Create pico_frame to contain the Neighbor Solicitation */
|
||||
sol = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
|
||||
if (!sol) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
sol->payload = sol->transport_hdr + len;
|
||||
sol->payload_len = 0;
|
||||
icmp = (struct pico_icmp6_hdr *)sol->transport_hdr;
|
||||
icmp->type = PICO_ICMP6_NEIGH_SOL;
|
||||
icmp->code = 0;
|
||||
icmp->msg.info.neigh_sol.unused = 0;
|
||||
icmp->msg.info.neigh_sol.target = *dst;
|
||||
return sol;
|
||||
}
|
||||
|
||||
/* RFC 4861 $7.2.2: sending neighbor solicitations */
|
||||
int pico_icmp6_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst)
|
||||
{
|
||||
struct pico_ip6 daddr = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00 }};
|
||||
struct pico_icmp6_opt_lladdr *llao = NULL;
|
||||
struct pico_icmp6_hdr *icmp = NULL;
|
||||
struct pico_frame *sol = NULL;
|
||||
uint8_t i = 0;
|
||||
uint16_t len = 0;
|
||||
#ifndef PICO_SUPPORT_6LOWPAN
|
||||
IGNORE_PARAMETER(dst);
|
||||
#endif
|
||||
|
||||
if (pico_ipv6_is_multicast(tgt->addr)) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
else if (PICO_DEV_IS_6LOWPAN(dev)) {
|
||||
return pico_6lp_nd_neighbor_solicitation(dev, tgt, type, dst);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
/* Determine the size frame needs to be for the Neighbor Solicitation */
|
||||
len = PICO_ICMP6HDR_NEIGH_SOL_SIZE;
|
||||
if (PICO_ICMP6_ND_DAD != type)
|
||||
len = (uint16_t)(len + 8);
|
||||
|
||||
/* Prepare a neighbor solicitation message */
|
||||
sol = pico_icmp6_neigh_sol_prep(dev, tgt, len);
|
||||
if (sol) {
|
||||
icmp = (struct pico_icmp6_hdr *)sol->transport_hdr;
|
||||
|
||||
/* Provide SLLAO if it's neighbor solicitation for DAD */
|
||||
llao = (struct pico_icmp6_opt_lladdr *)(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s));
|
||||
if (PICO_ICMP6_ND_DAD != type && pico_icmp6_provide_llao(llao, PICO_ND_OPT_LLADDR_SRC, dev, NULL)) {
|
||||
pico_frame_discard(sol);
|
||||
return -1;
|
||||
} else {
|
||||
/* Determine destination address */
|
||||
if (type == PICO_ICMP6_ND_SOLICITED || type == PICO_ICMP6_ND_DAD) {
|
||||
for (i = 1; i <= 3; ++i)
|
||||
daddr.addr[PICO_SIZE_IP6 - i] = tgt->addr[PICO_SIZE_IP6 - i];
|
||||
} else {
|
||||
daddr = *tgt;
|
||||
}
|
||||
|
||||
sol->dev = dev;
|
||||
/* f->src is set in frame_push, checksum calculated there */
|
||||
pico_ipv6_frame_push(sol, NULL, &daddr, PICO_PROTO_ICMP6, (type == PICO_ICMP6_ND_DAD));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
/* Provide an Address Registration Option */
|
||||
static void pico_6lp_nd_provide_aro(struct pico_icmp6_opt_aro *aro, struct pico_device *dev, uint8_t type)
|
||||
{
|
||||
struct pico_6lowpan_info *info = (struct pico_6lowpan_info *)dev->eth;
|
||||
aro->type = PICO_ND_OPT_ARO;
|
||||
aro->len = 2;
|
||||
aro->status = 0;
|
||||
if (PICO_ICMP6_ND_DEREGISTER == type)
|
||||
aro->lifetime = 0;
|
||||
else
|
||||
aro->lifetime = short_be(PICO_6LP_ND_DEFAULT_LIFETIME);
|
||||
memcpy(aro->eui64.addr, info->addr_ext.addr, SIZE_6LOWPAN_EXT);
|
||||
}
|
||||
|
||||
/* Send an ICMP6 neighbor solicitation according to RFC6775 */
|
||||
static int pico_6lp_nd_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst)
|
||||
{
|
||||
uint32_t llao_len = IID_16(&tgt->addr[8]) ? 8 : 16;
|
||||
struct pico_icmp6_opt_lladdr *llao = NULL;
|
||||
struct pico_icmp6_opt_aro *aro = NULL;
|
||||
struct pico_icmp6_hdr *icmp = NULL;
|
||||
struct pico_frame *sol = NULL;
|
||||
uint16_t len = 0;
|
||||
|
||||
/* Determine the size frame needs to be for the Neighbor Solicitation */
|
||||
len = (uint16_t)(PICO_ICMP6HDR_NEIGH_SOL_SIZE + llao_len);
|
||||
if (PICO_ICMP6_ND_DAD == type)
|
||||
len = (uint16_t)(len + sizeof(struct pico_icmp6_opt_aro));
|
||||
|
||||
/* Prepare a neighbor solicitation message */
|
||||
sol = pico_icmp6_neigh_sol_prep(dev, tgt, len);
|
||||
if (sol) {
|
||||
icmp = (struct pico_icmp6_hdr *)sol->transport_hdr;
|
||||
|
||||
/* Provide SLLAO if it's a neighbor solicitation for address registration */
|
||||
llao = (struct pico_icmp6_opt_lladdr *)(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s));
|
||||
if (pico_icmp6_provide_llao(llao, PICO_ND_OPT_LLADDR_SRC, dev, NULL)) {
|
||||
pico_frame_discard(sol);
|
||||
return -1;
|
||||
} else {
|
||||
/* Provide ARO when it's a neighbor solicitation for address registration or re-registration */
|
||||
aro = (struct pico_icmp6_opt_aro *)(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s) + llao_len);
|
||||
pico_6lp_nd_provide_aro(aro, dev, type);
|
||||
|
||||
/* RFC6775: The address that is to be registered MUST be the IPv6 source address of the
|
||||
* NS message. */
|
||||
sol->dev = dev;
|
||||
pico_ipv6_frame_push(sol, tgt, dst, PICO_PROTO_ICMP6, (type == PICO_ICMP6_ND_DAD));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* RFC 4861 $7.2.4: sending solicited neighbor advertisements */
|
||||
int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *target)
|
||||
{
|
||||
struct pico_frame *adv = NULL;
|
||||
struct pico_ipv6_hdr *ipv6_hdr = NULL;
|
||||
struct pico_icmp6_hdr *icmp6_hdr = NULL;
|
||||
struct pico_icmp6_opt_lladdr *opt = NULL;
|
||||
struct pico_ip6 dst = {{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}};
|
||||
|
||||
ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
adv = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, PICO_ICMP6HDR_NEIGH_ADV_SIZE + 8);
|
||||
if (!adv) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
adv->payload = adv->transport_hdr + PICO_ICMP6HDR_NEIGH_ADV_SIZE + 8;
|
||||
adv->payload_len = 0;
|
||||
|
||||
icmp6_hdr = (struct pico_icmp6_hdr *)adv->transport_hdr;
|
||||
icmp6_hdr->type = PICO_ICMP6_NEIGH_ADV;
|
||||
icmp6_hdr->code = 0;
|
||||
icmp6_hdr->msg.info.neigh_adv.target = *target;
|
||||
icmp6_hdr->msg.info.neigh_adv.rsor = long_be(0x60000000); /* !router && solicited && override */
|
||||
if (pico_ipv6_is_unspecified(ipv6_hdr->src.addr)) {
|
||||
/* solicited = clear && dst = all-nodes address (scope link-local) */
|
||||
icmp6_hdr->msg.info.neigh_adv.rsor ^= long_be(0x40000000);
|
||||
} else {
|
||||
/* solicited = set && dst = source of solicitation */
|
||||
dst = ipv6_hdr->src;
|
||||
}
|
||||
|
||||
/* XXX if the target address is either an anycast address or a unicast
|
||||
* address for which the node is providing proxy service, or the target
|
||||
* link-layer Address option is not included, the Override flag SHOULD
|
||||
* be set to zero.
|
||||
*/
|
||||
|
||||
/* XXX if the target address is an anycast address, the sender SHOULD delay
|
||||
* sending a response for a random time between 0 and MAX_ANYCAST_DELAY_TIME seconds.
|
||||
*/
|
||||
|
||||
opt = (struct pico_icmp6_opt_lladdr *)(((uint8_t *)&icmp6_hdr->msg.info.neigh_adv) + sizeof(struct neigh_adv_s));
|
||||
opt->type = PICO_ND_OPT_LLADDR_TGT;
|
||||
opt->len = 1;
|
||||
memcpy(opt->addr.mac.addr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
|
||||
/* f->src is set in frame_push, checksum calculated there */
|
||||
pico_ipv6_frame_push(adv, NULL, &dst, PICO_PROTO_ICMP6, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RFC 4861 $6.3.7: sending router solicitations */
|
||||
int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src, struct pico_ip6 *dst)
|
||||
{
|
||||
struct pico_ip6 daddr = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }};
|
||||
struct pico_icmp6_opt_lladdr *lladdr = NULL;
|
||||
struct pico_icmp6_hdr *icmp6_hdr = NULL;
|
||||
struct pico_frame *sol = NULL;
|
||||
uint16_t len = 0;
|
||||
|
||||
len = PICO_ICMP6HDR_ROUTER_SOL_SIZE;
|
||||
if (!pico_ipv6_is_unspecified(src->addr)) {
|
||||
len = (uint16_t)(len + 8);
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
if (PICO_DEV_IS_6LOWPAN(dev))
|
||||
len = (uint16_t)(len + 8);
|
||||
} else if (PICO_DEV_IS_6LOWPAN(dev) && pico_ipv6_is_unspecified(src->addr)) {
|
||||
return -1; /* RFC6775 (6LoWPAN): An unspecified source address MUST NOT be used in RS messages. */
|
||||
#endif
|
||||
}
|
||||
|
||||
sol = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
|
||||
if (!sol) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
sol->payload = sol->transport_hdr + len;
|
||||
sol->payload_len = 0;
|
||||
|
||||
icmp6_hdr = (struct pico_icmp6_hdr *)sol->transport_hdr;
|
||||
icmp6_hdr->type = PICO_ICMP6_ROUTER_SOL;
|
||||
icmp6_hdr->code = 0;
|
||||
|
||||
if (!pico_ipv6_is_unspecified(src->addr)) {
|
||||
lladdr = (struct pico_icmp6_opt_lladdr *)((uint8_t *)&icmp6_hdr->msg.info.router_sol + sizeof(struct router_sol_s));
|
||||
if (pico_icmp6_provide_llao(lladdr, PICO_ND_OPT_LLADDR_SRC, dev, NULL)) {
|
||||
pico_frame_discard(sol);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
sol->dev = dev;
|
||||
|
||||
if (!dev->mode) {
|
||||
/* f->src is set in frame_push, checksum calculated there */
|
||||
pico_ipv6_frame_push(sol, NULL, &daddr, PICO_PROTO_ICMP6, 0);
|
||||
}
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
else {
|
||||
if (dst)
|
||||
daddr = *dst;
|
||||
/* Force this frame to be send with the EUI-64-address */
|
||||
pico_ipv6_frame_push(sol, src, &daddr, PICO_PROTO_ICMP6, 0);
|
||||
}
|
||||
#else
|
||||
IGNORE_PARAMETER(dst);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PICO_RADV_VAL_LIFETIME (long_be(86400))
|
||||
#define PICO_RADV_PREF_LIFETIME (long_be(14400))
|
||||
|
||||
static struct pico_ip6 pico_icmp6_address_to_prefix(struct pico_ip6 addr, struct pico_ip6 nm)
|
||||
{
|
||||
struct pico_ip6 prefix;
|
||||
uint8_t i = 0;
|
||||
|
||||
for (i = 0; i < PICO_SIZE_IP6; i++) {
|
||||
prefix.addr[i] = (uint8_t)(addr.addr[i] & nm.addr[i]);
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/* RFC 4861: sending router advertisements */
|
||||
int pico_icmp6_router_advertisement(struct pico_device *dev, struct pico_ip6 *dst)
|
||||
{
|
||||
struct pico_frame *adv = NULL;
|
||||
struct pico_ip6 prefix_addr = {{ 0x00 }};
|
||||
struct pico_icmp6_hdr *icmp6_hdr = NULL;
|
||||
struct pico_icmp6_opt_lladdr *lladdr;
|
||||
struct pico_icmp6_opt_prefix *prefix;
|
||||
struct pico_ipv6_link *global = NULL;
|
||||
uint16_t len = 0;
|
||||
uint8_t *nxt_opt;
|
||||
|
||||
len = PICO_ICMP6HDR_ROUTER_ADV_SIZE + PICO_ICMP6_OPT_LLADDR_SIZE + sizeof(struct pico_icmp6_opt_prefix);
|
||||
|
||||
adv = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
|
||||
if (!adv) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
adv->payload = adv->transport_hdr + len;
|
||||
adv->payload_len = 0;
|
||||
|
||||
icmp6_hdr = (struct pico_icmp6_hdr *)adv->transport_hdr;
|
||||
icmp6_hdr->type = PICO_ICMP6_ROUTER_ADV;
|
||||
icmp6_hdr->code = 0;
|
||||
icmp6_hdr->msg.info.router_adv.life_time = short_be(45);
|
||||
icmp6_hdr->msg.info.router_adv.hop = 64;
|
||||
nxt_opt = (uint8_t *)&icmp6_hdr->msg.info.router_adv + sizeof(struct router_adv_s);
|
||||
|
||||
prefix = (struct pico_icmp6_opt_prefix *)nxt_opt;
|
||||
prefix->type = PICO_ND_OPT_PREFIX;
|
||||
prefix->len = sizeof(struct pico_icmp6_opt_prefix) >> 3;
|
||||
prefix->prefix_len = 64; /* Only /64 are forwarded */
|
||||
prefix->aac = 1;
|
||||
prefix->onlink = 1;
|
||||
prefix->val_lifetime = PICO_RADV_VAL_LIFETIME;
|
||||
prefix->pref_lifetime = PICO_RADV_PREF_LIFETIME;
|
||||
/* Find the globally routable prefix of the router-interface */
|
||||
if ((global = pico_ipv6_global_get(dev))) {
|
||||
prefix_addr = pico_icmp6_address_to_prefix(global->address, global->netmask);
|
||||
memcpy(&prefix->prefix, &prefix_addr, sizeof(struct pico_ip6));
|
||||
}
|
||||
|
||||
nxt_opt += (sizeof (struct pico_icmp6_opt_prefix));
|
||||
lladdr = (struct pico_icmp6_opt_lladdr *)nxt_opt;
|
||||
|
||||
lladdr->type = PICO_ND_OPT_LLADDR_SRC;
|
||||
|
||||
if (!dev->mode && dev->eth) {
|
||||
lladdr->len = 1;
|
||||
memcpy(lladdr->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
} else {
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
icmp6_hdr->crc = 0;
|
||||
icmp6_hdr->crc = short_be(pico_icmp6_checksum(adv));
|
||||
/* f->src is set in frame_push, checksum calculated there */
|
||||
pico_ipv6_frame_push(adv, NULL, dst, PICO_PROTO_ICMP6, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* Ping implementation */
|
||||
/***********************/
|
||||
|
||||
#ifdef PICO_SUPPORT_PING
|
||||
struct pico_icmp6_ping_cookie
|
||||
{
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
uint16_t size;
|
||||
uint16_t err;
|
||||
int count;
|
||||
int interval;
|
||||
int timeout;
|
||||
pico_time timestamp;
|
||||
struct pico_ip6 dst;
|
||||
struct pico_device *dev;
|
||||
void (*cb)(struct pico_icmp6_stats*);
|
||||
};
|
||||
|
||||
static int icmp6_cookie_compare(void *ka, void *kb)
|
||||
{
|
||||
struct pico_icmp6_ping_cookie *a = ka, *b = kb;
|
||||
if (a->id < b->id)
|
||||
return -1;
|
||||
|
||||
if (a->id > b->id)
|
||||
return 1;
|
||||
|
||||
return (a->seq - b->seq);
|
||||
}
|
||||
static PICO_TREE_DECLARE(IPV6Pings, icmp6_cookie_compare);
|
||||
|
||||
static int pico_icmp6_send_echo(struct pico_icmp6_ping_cookie *cookie)
|
||||
{
|
||||
struct pico_frame *echo = NULL;
|
||||
struct pico_icmp6_hdr *hdr = NULL;
|
||||
|
||||
echo = pico_proto_ipv6.alloc(&pico_proto_ipv6, cookie->dev, (uint16_t)(PICO_ICMP6HDR_ECHO_REQUEST_SIZE + cookie->size));
|
||||
if (!echo) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
echo->payload = echo->transport_hdr + PICO_ICMP6HDR_ECHO_REQUEST_SIZE;
|
||||
echo->payload_len = cookie->size;
|
||||
|
||||
hdr = (struct pico_icmp6_hdr *)echo->transport_hdr;
|
||||
hdr->type = PICO_ICMP6_ECHO_REQUEST;
|
||||
hdr->code = 0;
|
||||
hdr->msg.info.echo_request.id = short_be(cookie->id);
|
||||
hdr->msg.info.echo_request.seq = short_be(cookie->seq);
|
||||
/* XXX: Fill payload */
|
||||
hdr->crc = 0;
|
||||
hdr->crc = short_be(pico_icmp6_checksum(echo));
|
||||
pico_ipv6_frame_push(echo, NULL, &cookie->dst, PICO_PROTO_ICMP6, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void pico_icmp6_ping_timeout(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_icmp6_ping_cookie *cookie = NULL;
|
||||
|
||||
IGNORE_PARAMETER(now);
|
||||
|
||||
cookie = (struct pico_icmp6_ping_cookie *)arg;
|
||||
if (pico_tree_findKey(&IPV6Pings, cookie)) {
|
||||
if (cookie->err == PICO_PING6_ERR_PENDING) {
|
||||
struct pico_icmp6_stats stats = {
|
||||
0
|
||||
};
|
||||
stats.dst = cookie->dst;
|
||||
stats.seq = cookie->seq;
|
||||
stats.time = 0;
|
||||
stats.size = cookie->size;
|
||||
stats.err = PICO_PING6_ERR_TIMEOUT;
|
||||
dbg(" ---- Ping6 timeout!!!\n");
|
||||
if (cookie->cb)
|
||||
cookie->cb(&stats);
|
||||
}
|
||||
|
||||
pico_tree_delete(&IPV6Pings, cookie);
|
||||
PICO_FREE(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
static void pico_icmp6_next_ping(pico_time now, void *arg);
|
||||
static int pico_icmp6_send_ping(struct pico_icmp6_ping_cookie *cookie)
|
||||
{
|
||||
uint32_t interval_timer = 0;
|
||||
struct pico_icmp6_stats stats;
|
||||
pico_icmp6_send_echo(cookie);
|
||||
cookie->timestamp = pico_tick;
|
||||
interval_timer = pico_timer_add((pico_time)(cookie->interval), pico_icmp6_next_ping, cookie);
|
||||
if (!interval_timer) {
|
||||
goto fail;
|
||||
}
|
||||
if (!pico_timer_add((pico_time)(cookie->timeout), pico_icmp6_ping_timeout, cookie)) {
|
||||
pico_timer_cancel(interval_timer);
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
dbg("ICMP6: Failed to start timer\n");
|
||||
cookie->err = PICO_PING6_ERR_ABORTED;
|
||||
stats.err = cookie->err;
|
||||
cookie->cb(&stats);
|
||||
pico_tree_delete(&IPV6Pings, cookie);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pico_icmp6_next_ping(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_icmp6_ping_cookie *cookie = NULL, *new = NULL;
|
||||
|
||||
IGNORE_PARAMETER(now);
|
||||
|
||||
cookie = (struct pico_icmp6_ping_cookie *)arg;
|
||||
if (pico_tree_findKey(&IPV6Pings, cookie)) {
|
||||
if (cookie->err == PICO_PING6_ERR_ABORTED)
|
||||
return;
|
||||
|
||||
if (cookie->seq < (uint16_t)cookie->count) {
|
||||
new = PICO_ZALLOC(sizeof(struct pico_icmp6_ping_cookie));
|
||||
if (!new) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(new, cookie, sizeof(struct pico_icmp6_ping_cookie));
|
||||
new->seq++;
|
||||
|
||||
if (pico_tree_insert(&IPV6Pings, new)) {
|
||||
dbg("ICMP6: Failed to insert new cookie in tree\n");
|
||||
PICO_FREE(new);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pico_icmp6_send_ping(new)) {
|
||||
dbg("ICMP6: Failed to send ping\n");
|
||||
PICO_FREE(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pico_icmp6_ping_recv_reply(struct pico_frame *f)
|
||||
{
|
||||
struct pico_icmp6_ping_cookie *cookie = NULL, test = {
|
||||
0
|
||||
};
|
||||
struct pico_icmp6_hdr *hdr = NULL;
|
||||
|
||||
hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
|
||||
test.id = short_be(hdr->msg.info.echo_reply.id);
|
||||
test.seq = short_be(hdr->msg.info.echo_reply.seq);
|
||||
cookie = pico_tree_findKey(&IPV6Pings, &test);
|
||||
if (cookie) {
|
||||
struct pico_icmp6_stats stats = {
|
||||
0
|
||||
};
|
||||
if (cookie->err == PICO_PING6_ERR_ABORTED)
|
||||
return;
|
||||
|
||||
cookie->err = PICO_PING6_ERR_REPLIED;
|
||||
stats.dst = cookie->dst;
|
||||
stats.seq = cookie->seq;
|
||||
stats.size = cookie->size;
|
||||
stats.time = pico_tick - cookie->timestamp;
|
||||
stats.err = cookie->err;
|
||||
stats.ttl = ((struct pico_ipv6_hdr *)f->net_hdr)->hop;
|
||||
if(cookie->cb)
|
||||
cookie->cb(&stats);
|
||||
} else {
|
||||
dbg("Reply for seq=%d, not found.\n", test.seq);
|
||||
}
|
||||
}
|
||||
|
||||
int pico_icmp6_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp6_stats *), struct pico_device *dev)
|
||||
{
|
||||
static uint16_t next_id = 0x91c0;
|
||||
struct pico_icmp6_ping_cookie *cookie = NULL;
|
||||
|
||||
if(!dst || !count || !interval || !timeout) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cookie = PICO_ZALLOC(sizeof(struct pico_icmp6_ping_cookie));
|
||||
if (!cookie) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_string_to_ipv6(dst, cookie->dst.addr) < 0) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
PICO_FREE(cookie);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cookie->seq = 1;
|
||||
cookie->id = next_id++;
|
||||
cookie->err = PICO_PING6_ERR_PENDING;
|
||||
cookie->size = (uint16_t)size;
|
||||
cookie->interval = interval;
|
||||
cookie->timeout = timeout;
|
||||
cookie->cb = cb;
|
||||
cookie->count = count;
|
||||
cookie->dev = dev;
|
||||
|
||||
if (pico_tree_insert(&IPV6Pings, cookie)) {
|
||||
dbg("ICMP6: Failed to insert cookie in tree\n");
|
||||
PICO_FREE(cookie);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pico_icmp6_send_ping(cookie)) {
|
||||
PICO_FREE(cookie);
|
||||
return -1;
|
||||
}
|
||||
return (int)cookie->id;
|
||||
}
|
||||
|
||||
int pico_icmp6_ping_abort(int id)
|
||||
{
|
||||
struct pico_tree_node *node;
|
||||
int found = 0;
|
||||
pico_tree_foreach(node, &IPV6Pings)
|
||||
{
|
||||
struct pico_icmp6_ping_cookie *ck =
|
||||
(struct pico_icmp6_ping_cookie *) node->keyValue;
|
||||
if (ck->id == (uint16_t)id) {
|
||||
ck->err = PICO_PING6_ERR_ABORTED;
|
||||
found++;
|
||||
}
|
||||
}
|
||||
if (found > 0)
|
||||
return 0; /* OK if at least one pending ping has been canceled */
|
||||
|
||||
pico_err = PICO_ERR_ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
326
kernel/picotcp/modules/pico_icmp6.h
Normal file
326
kernel/picotcp/modules/pico_icmp6.h
Normal file
@ -0,0 +1,326 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef _INCLUDE_PICO_ICMP6
|
||||
#define _INCLUDE_PICO_ICMP6
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_mld.h"
|
||||
/* ICMP header sizes */
|
||||
#define PICO_ICMP6HDR_DRY_SIZE 4
|
||||
#define PICO_ICMP6HDR_ECHO_REQUEST_SIZE 8
|
||||
#define PICO_ICMP6HDR_DEST_UNREACH_SIZE 8
|
||||
#define PICO_ICMP6HDR_TIME_XCEEDED_SIZE 8
|
||||
#define PICO_ICMP6HDR_PARAM_PROBLEM_SIZE 8
|
||||
#define PICO_ICMP6HDR_NEIGH_SOL_SIZE 24
|
||||
#define PICO_ICMP6HDR_NEIGH_ADV_SIZE 24
|
||||
#define PICO_ICMP6HDR_ROUTER_SOL_SIZE 8
|
||||
#define PICO_ICMP6HDR_ROUTER_SOL_SIZE_6LP 16
|
||||
#define PICO_ICMP6HDR_ROUTER_ADV_SIZE 16
|
||||
#define PICO_ICMP6HDR_REDIRECT_SIZE 40
|
||||
|
||||
/* ICMP types */
|
||||
#define PICO_ICMP6_DEST_UNREACH 1
|
||||
#define PICO_ICMP6_PKT_TOO_BIG 2
|
||||
#define PICO_ICMP6_TIME_EXCEEDED 3
|
||||
#define PICO_ICMP6_PARAM_PROBLEM 4
|
||||
#define PICO_ICMP6_ECHO_REQUEST 128
|
||||
#define PICO_ICMP6_ECHO_REPLY 129
|
||||
#define PICO_ICMP6_ROUTER_SOL 133
|
||||
#define PICO_ICMP6_ROUTER_ADV 134
|
||||
#define PICO_ICMP6_NEIGH_SOL 135
|
||||
#define PICO_ICMP6_NEIGH_ADV 136
|
||||
#define PICO_ICMP6_REDIRECT 137
|
||||
|
||||
/* destination unreachable codes */
|
||||
#define PICO_ICMP6_UNREACH_NOROUTE 0
|
||||
#define PICO_ICMP6_UNREACH_ADMIN 1
|
||||
#define PICO_ICMP6_UNREACH_SRCSCOPE 2
|
||||
#define PICO_ICMP6_UNREACH_ADDR 3
|
||||
#define PICO_ICMP6_UNREACH_PORT 4
|
||||
#define PICO_ICMP6_UNREACH_SRCFILTER 5
|
||||
#define PICO_ICMP6_UNREACH_REJROUTE 6
|
||||
|
||||
/* time exceeded codes */
|
||||
#define PICO_ICMP6_TIMXCEED_INTRANS 0
|
||||
#define PICO_ICMP6_TIMXCEED_REASS 1
|
||||
|
||||
/* parameter problem codes */
|
||||
#define PICO_ICMP6_PARAMPROB_HDRFIELD 0
|
||||
#define PICO_ICMP6_PARAMPROB_NXTHDR 1
|
||||
#define PICO_ICMP6_PARAMPROB_IPV6OPT 2
|
||||
|
||||
/* ping error codes */
|
||||
#define PICO_PING6_ERR_REPLIED 0
|
||||
#define PICO_PING6_ERR_TIMEOUT 1
|
||||
#define PICO_PING6_ERR_UNREACH 2
|
||||
#define PICO_PING6_ERR_ABORTED 3
|
||||
#define PICO_PING6_ERR_PENDING 0xFFFF
|
||||
|
||||
/* ND configuration */
|
||||
#define PICO_ND_MAX_FRAMES_QUEUED 4 /* max frames queued while awaiting address resolution */
|
||||
|
||||
/* ND RFC constants */
|
||||
#define PICO_ND_MAX_SOLICIT 3
|
||||
#define PICO_ND_MAX_NEIGHBOR_ADVERT 3
|
||||
#define PICO_ND_DELAY_INCOMPLETE 1000 /* msec */
|
||||
#define PICO_ND_DELAY_FIRST_PROBE_TIME 5000 /* msec */
|
||||
|
||||
/* neighbor discovery options */
|
||||
#define PICO_ND_OPT_LLADDR_SRC 1
|
||||
#define PICO_ND_OPT_LLADDR_TGT 2
|
||||
#define PICO_ND_OPT_PREFIX 3
|
||||
#define PICO_ND_OPT_REDIRECT 4
|
||||
#define PICO_ND_OPT_MTU 5
|
||||
#define PICO_ND_OPT_RDNSS 25 /* RFC 5006 */
|
||||
#define PICO_ND_OPT_ARO 33 /* RFC 6775 */
|
||||
#define PICO_ND_OPT_6CO 34 /* RFC 6775 */
|
||||
#define PICO_ND_OPT_ABRO 35 /* RFC 6775 */
|
||||
|
||||
/* ND advertisement flags */
|
||||
#define PICO_ND_ROUTER 0x80000000
|
||||
#define PICO_ND_SOLICITED 0x40000000
|
||||
#define PICO_ND_OVERRIDE 0x20000000
|
||||
#define IS_ROUTER(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_ROUTER)) /* router flag set? */
|
||||
#define IS_SOLICITED(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_SOLICITED)) /* solicited flag set? */
|
||||
#define IS_OVERRIDE(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_OVERRIDE)) /* override flag set? */
|
||||
|
||||
#define PICO_ND_PREFIX_LIFETIME_INF 0xFFFFFFFFu
|
||||
/* #define PICO_ND_DESTINATION_LRU_TIME 600000u / * msecs (10min) * / */
|
||||
|
||||
/* custom defines */
|
||||
#define PICO_ICMP6_ND_UNICAST 0
|
||||
#define PICO_ICMP6_ND_ANYCAST 1
|
||||
#define PICO_ICMP6_ND_SOLICITED 2
|
||||
#define PICO_ICMP6_ND_DAD 3
|
||||
#define PICO_ICMP6_ND_DEREGISTER 4
|
||||
|
||||
#define PICO_ICMP6_MAX_RTR_SOL_DELAY 1000
|
||||
|
||||
#define PICO_ICMP6_OPT_LLADDR_SIZE (8)
|
||||
|
||||
/******************************************************************************
|
||||
* 6LoWPAN Constants
|
||||
******************************************************************************/
|
||||
|
||||
/* Address registration lifetime */
|
||||
#define PICO_6LP_ND_DEFAULT_LIFETIME (120) /* TWO HOURS */
|
||||
|
||||
extern struct pico_protocol pico_proto_icmp6;
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp6_hdr {
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t crc;
|
||||
|
||||
PACKED_UNION_DEF icmp6_msg_u {
|
||||
/* error messages */
|
||||
PACKED_UNION_DEF icmp6_err_u {
|
||||
PEDANTIC_STRUCT_DEF dest_unreach_s {
|
||||
uint32_t unused;
|
||||
} dest_unreach;
|
||||
PEDANTIC_STRUCT_DEF pkt_too_big_s {
|
||||
uint32_t mtu;
|
||||
} pkt_too_big;
|
||||
PEDANTIC_STRUCT_DEF time_exceeded_s {
|
||||
uint32_t unused;
|
||||
} time_exceeded;
|
||||
PEDANTIC_STRUCT_DEF param_problem_s {
|
||||
uint32_t ptr;
|
||||
} param_problem;
|
||||
} err;
|
||||
|
||||
/* informational messages */
|
||||
PACKED_UNION_DEF icmp6_info_u {
|
||||
PEDANTIC_STRUCT_DEF echo_request_s {
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
} echo_request;
|
||||
PEDANTIC_STRUCT_DEF echo_reply_s {
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
} echo_reply;
|
||||
PEDANTIC_STRUCT_DEF router_sol_s {
|
||||
uint32_t unused;
|
||||
} router_sol;
|
||||
PEDANTIC_STRUCT_DEF router_adv_s {
|
||||
uint8_t hop;
|
||||
uint8_t mor;
|
||||
uint16_t life_time;
|
||||
uint32_t reachable_time;
|
||||
uint32_t retrans_time;
|
||||
} router_adv;
|
||||
PEDANTIC_STRUCT_DEF neigh_sol_s {
|
||||
uint32_t unused;
|
||||
struct pico_ip6 target;
|
||||
} neigh_sol;
|
||||
PEDANTIC_STRUCT_DEF neigh_adv_s {
|
||||
uint32_t rsor;
|
||||
struct pico_ip6 target;
|
||||
} neigh_adv;
|
||||
PEDANTIC_STRUCT_DEF redirect_s {
|
||||
uint32_t reserved;
|
||||
struct pico_ip6 target;
|
||||
struct pico_ip6 dest;
|
||||
} redirect;
|
||||
PEDANTIC_STRUCT_DEF mld_s {
|
||||
uint16_t max_resp_time;
|
||||
uint16_t reserved;
|
||||
struct pico_ip6 mmcast_group;
|
||||
/*MLDv2*/
|
||||
uint8_t reserverd; /* With S and QRV */
|
||||
uint8_t QQIC;
|
||||
uint16_t nbr_src;
|
||||
struct pico_ip6 src[1];
|
||||
} mld;
|
||||
/* 6LoWPAN Duplicate Address Message */
|
||||
PEDANTIC_STRUCT_DEF da_s {
|
||||
uint8_t status;
|
||||
uint8_t reserved;
|
||||
uint16_t lifetime;
|
||||
struct pico_6lowpan_ext eui64;
|
||||
struct pico_ip6 addr;
|
||||
} da;
|
||||
} info;
|
||||
} msg;
|
||||
};
|
||||
|
||||
PACKED_UNION_DEF pico_hw_addr {
|
||||
struct pico_eth mac;
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
union pico_6lowpan_u pan;
|
||||
#endif /* PICO_SUPPORT_6LOWPAN */
|
||||
uint8_t data[8];
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* ICMP6 Neighbor Discovery Options
|
||||
******************************************************************************/
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_lladdr
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
union pico_hw_addr addr;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_prefix
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint8_t prefix_len;
|
||||
uint8_t res : 6;
|
||||
uint8_t aac : 1;
|
||||
uint8_t onlink : 1;
|
||||
uint32_t val_lifetime;
|
||||
uint32_t pref_lifetime;
|
||||
uint32_t reserved;
|
||||
struct pico_ip6 prefix;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_mtu
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint16_t res;
|
||||
uint32_t mtu;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_redirect
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint16_t res0;
|
||||
uint32_t res1;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_rdnss
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint16_t res0;
|
||||
uint32_t lifetime;
|
||||
struct pico_ip6 *addr;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_na
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
};
|
||||
|
||||
/* 6LoWPAN Address Registration Option (ARO) */
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_aro
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint8_t status;
|
||||
uint8_t res0;
|
||||
uint16_t res1;
|
||||
uint16_t lifetime;
|
||||
struct pico_6lowpan_ext eui64;
|
||||
};
|
||||
|
||||
#define ICMP6_ARO_SUCCES (0u)
|
||||
#define ICMP6_ARO_DUP (1u)
|
||||
#define ICMP6_ARO_FULL (2u)
|
||||
|
||||
/* 6LoWPAN Context Option (6CO) */
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_6co
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint8_t clen;
|
||||
uint8_t id: 4;
|
||||
uint8_t res: 3;
|
||||
uint8_t c: 1;
|
||||
uint16_t lifetime;
|
||||
uint8_t prefix;
|
||||
};
|
||||
|
||||
/* 6LoWPAN Authoritative Border Router Option (ABRO) */
|
||||
PACKED_STRUCT_DEF pico_icmp6_opt_abro
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint16_t version_low;
|
||||
uint16_t version_high;
|
||||
uint16_t lifetime;
|
||||
struct pico_ip6 addr;
|
||||
};
|
||||
|
||||
struct pico_icmp6_stats
|
||||
{
|
||||
unsigned long size;
|
||||
unsigned long seq;
|
||||
pico_time time;
|
||||
unsigned long ttl;
|
||||
int err;
|
||||
struct pico_ip6 dst;
|
||||
};
|
||||
|
||||
int pico_icmp6_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp6_stats *), struct pico_device *dev);
|
||||
int pico_icmp6_ping_abort(int id);
|
||||
|
||||
|
||||
int pico_icmp6_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst);
|
||||
int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *target);
|
||||
int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src, struct pico_ip6 *dst);
|
||||
|
||||
int pico_icmp6_port_unreachable(struct pico_frame *f);
|
||||
int pico_icmp6_proto_unreachable(struct pico_frame *f);
|
||||
int pico_icmp6_dest_unreachable(struct pico_frame *f);
|
||||
int pico_icmp6_ttl_expired(struct pico_frame *f);
|
||||
int pico_icmp6_packet_filtered(struct pico_frame *f);
|
||||
int pico_icmp6_parameter_problem(struct pico_frame *f, uint8_t problem, uint32_t ptr);
|
||||
int pico_icmp6_pkt_too_big(struct pico_frame *f);
|
||||
int pico_icmp6_frag_expired(struct pico_frame *f);
|
||||
|
||||
uint16_t pico_icmp6_checksum(struct pico_frame *f);
|
||||
int pico_icmp6_router_advertisement(struct pico_device *dev, struct pico_ip6 *dst);
|
||||
|
||||
#endif
|
||||
1170
kernel/picotcp/modules/pico_igmp.c
Normal file
1170
kernel/picotcp/modules/pico_igmp.c
Normal file
File diff suppressed because it is too large
Load Diff
26
kernel/picotcp/modules/pico_igmp.h
Normal file
26
kernel/picotcp/modules/pico_igmp.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_IGMP
|
||||
#define INCLUDE_PICO_IGMP
|
||||
|
||||
#define PICO_IGMPV1 1
|
||||
#define PICO_IGMPV2 2
|
||||
#define PICO_IGMPV3 3
|
||||
|
||||
#define PICO_IGMP_STATE_CREATE 1
|
||||
#define PICO_IGMP_STATE_UPDATE 2
|
||||
#define PICO_IGMP_STATE_DELETE 3
|
||||
|
||||
#define PICO_IGMP_QUERY_INTERVAL 125
|
||||
|
||||
extern struct pico_protocol pico_proto_igmp;
|
||||
|
||||
int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *_MCASTFilter, uint8_t state);
|
||||
#endif /* _INCLUDE_PICO_IGMP */
|
||||
464
kernel/picotcp/modules/pico_ipfilter.c
Normal file
464
kernel/picotcp/modules/pico_ipfilter.c
Normal file
@ -0,0 +1,464 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Andrei Carp
|
||||
Simon Maes
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_icmp4.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_eth.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_ipfilter.h"
|
||||
#include "pico_tcp.h"
|
||||
#include "pico_udp.h"
|
||||
#include "pico_tree.h"
|
||||
|
||||
/**************** LOCAL MACROS ****************/
|
||||
#define MAX_PRIORITY (10)
|
||||
#define MIN_PRIORITY (-10)
|
||||
|
||||
#ifdef DEBUG_IPF
|
||||
#define ipf_dbg dbg
|
||||
#else
|
||||
#define ipf_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/**************** LOCAL DECLARATIONS ****************/
|
||||
struct filter_node;
|
||||
static int filter_compare(void *filterA, void *filterB);
|
||||
|
||||
/**************** FILTER TREE ****************/
|
||||
|
||||
struct filter_node {
|
||||
struct pico_device *fdev;
|
||||
/* output address */
|
||||
uint32_t out_addr;
|
||||
uint32_t out_addr_netmask;
|
||||
/* input address */
|
||||
uint32_t in_addr;
|
||||
uint32_t in_addr_netmask;
|
||||
/* transport */
|
||||
uint16_t out_port;
|
||||
uint16_t in_port;
|
||||
/* filter details */
|
||||
uint8_t proto;
|
||||
int8_t priority;
|
||||
uint8_t tos;
|
||||
uint32_t filter_id;
|
||||
int (*function_ptr)(struct filter_node *filter, struct pico_frame *f);
|
||||
};
|
||||
|
||||
static PICO_TREE_DECLARE(filter_tree, &filter_compare);
|
||||
|
||||
static inline int ipfilter_uint32_cmp(uint32_t a, uint32_t b)
|
||||
{
|
||||
if (a < b)
|
||||
return -1;
|
||||
|
||||
if (b < a)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ipfilter_uint16_cmp(uint16_t a, uint16_t b)
|
||||
{
|
||||
if (a < b)
|
||||
return -1;
|
||||
|
||||
if (b < a)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ipfilter_uint8_cmp(uint8_t a, uint8_t b)
|
||||
{
|
||||
if (a < b)
|
||||
return -1;
|
||||
|
||||
if (b < a)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ipfilter_ptr_cmp(void *a, void *b)
|
||||
{
|
||||
if (a < b)
|
||||
return -1;
|
||||
|
||||
if (b < a)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline int filter_compare_ports(struct filter_node *a, struct filter_node *b)
|
||||
{
|
||||
int cmp;
|
||||
cmp = ipfilter_uint16_cmp(a->in_port, b->in_port);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
cmp = ipfilter_uint16_cmp(a->out_port, b->out_port);
|
||||
return cmp;
|
||||
}
|
||||
|
||||
static inline int filter_compare_addresses(struct filter_node *a, struct filter_node *b)
|
||||
{
|
||||
int cmp;
|
||||
/* Compare source address */
|
||||
cmp = ipfilter_uint32_cmp((a->in_addr & a->in_addr_netmask), (b->in_addr & b->in_addr_netmask));
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
/* Compare destination address */
|
||||
cmp = ipfilter_uint32_cmp((a->out_addr & a->out_addr_netmask), (b->out_addr & b->out_addr_netmask));
|
||||
return cmp;
|
||||
}
|
||||
|
||||
static inline int filter_compare_proto(struct filter_node *a, struct filter_node *b)
|
||||
{
|
||||
return ipfilter_uint8_cmp(a->proto, b->proto);
|
||||
}
|
||||
|
||||
static inline int filter_compare_address_port(struct filter_node *a, struct filter_node *b)
|
||||
{
|
||||
int cmp;
|
||||
cmp = filter_compare_addresses(a, b);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
return filter_compare_ports(a, b);
|
||||
}
|
||||
|
||||
static inline int filter_match_packet_dev(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp;
|
||||
/* 1. Compare devices */
|
||||
if (rule->fdev) {
|
||||
cmp = ipfilter_ptr_cmp(a->fdev, b->fdev);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static inline int filter_match_packet_proto(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp;
|
||||
/* 2. Compare protocol */
|
||||
if (rule->proto) {
|
||||
cmp = filter_compare_proto(a, b);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
static inline int filter_match_packet_addr_in(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp;
|
||||
/* 3. Compare addresses order: in, out */
|
||||
if (rule->in_addr_netmask) {
|
||||
cmp = ipfilter_uint32_cmp(a->in_addr & rule->in_addr_netmask, b->in_addr & rule->in_addr_netmask);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static inline int filter_match_packet_addr_out(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp;
|
||||
if (rule->out_addr_netmask) {
|
||||
cmp = ipfilter_uint32_cmp(a->out_addr & rule->out_addr_netmask, b->out_addr & rule->out_addr_netmask);
|
||||
if (cmp) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static inline int filter_match_packet_port_in(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp;
|
||||
/* 4. Compare ports order: in, out */
|
||||
if (rule->in_port) {
|
||||
cmp = ipfilter_uint16_cmp(a->in_port, b->in_port);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static inline int filter_match_packet_port_out(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp;
|
||||
if (rule->out_port) {
|
||||
cmp = ipfilter_uint16_cmp(a->out_port, b->out_port);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int filter_match_packet_dev_and_proto(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp = filter_match_packet_dev(a, b, rule);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
return filter_match_packet_proto(a, b, rule);
|
||||
}
|
||||
|
||||
static inline int filter_match_packet_addr(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp = filter_match_packet_addr_in(a, b, rule);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
return filter_match_packet_addr_out(a, b, rule);
|
||||
|
||||
}
|
||||
|
||||
static inline int filter_match_packet_port(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
|
||||
{
|
||||
int cmp = filter_match_packet_port_in(a, b, rule);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
return filter_match_packet_port_out(a, b, rule);
|
||||
}
|
||||
|
||||
static inline struct filter_node *filter_match_packet_find_rule(struct filter_node *a, struct filter_node *b)
|
||||
{
|
||||
if (!a->filter_id)
|
||||
return b;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static inline int filter_match_packet(struct filter_node *a, struct filter_node *b)
|
||||
{
|
||||
struct filter_node *rule;
|
||||
int cmp = 0;
|
||||
rule = filter_match_packet_find_rule(a, b);
|
||||
|
||||
cmp = filter_match_packet_dev_and_proto(a, b, rule);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
cmp = filter_match_packet_addr(a, b, rule);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
cmp = filter_match_packet_port(a, b, rule);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int filter_compare(void *filterA, void *filterB)
|
||||
{
|
||||
|
||||
struct filter_node *a = (struct filter_node *)filterA;
|
||||
struct filter_node *b = (struct filter_node *)filterB;
|
||||
int cmp = 0;
|
||||
if (a->filter_id == 0 || b->filter_id == 0) {
|
||||
return filter_match_packet(a, b);
|
||||
}
|
||||
|
||||
/* improve the search */
|
||||
if(a->filter_id == b->filter_id)
|
||||
return 0;
|
||||
|
||||
/* 1. Compare devices */
|
||||
cmp = ipfilter_ptr_cmp(a->fdev, a->fdev);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
/* 2. Compare protocol */
|
||||
cmp = filter_compare_proto(a, b);
|
||||
if(cmp)
|
||||
return cmp;
|
||||
|
||||
/* 3. Compare addresses order: in, out */
|
||||
/* 4. Compare ports order: in, out */
|
||||
cmp = filter_compare_address_port(a, b);
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
/**************** FILTER CALLBACKS ****************/
|
||||
|
||||
static int fp_priority(struct filter_node *filter, struct pico_frame *f)
|
||||
{
|
||||
/* TODO do priority-stuff */
|
||||
IGNORE_PARAMETER(filter);
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fp_reject(struct filter_node *filter, struct pico_frame *f)
|
||||
{
|
||||
/* TODO check first if sender is pico itself or not */
|
||||
IGNORE_PARAMETER(filter);
|
||||
ipf_dbg("ipfilter> reject\n");
|
||||
(void)pico_icmp4_packet_filtered(f);
|
||||
pico_frame_discard(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fp_drop(struct filter_node *filter, struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(filter);
|
||||
ipf_dbg("ipfilter> drop\n");
|
||||
pico_frame_discard(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct fp_function {
|
||||
int (*fn)(struct filter_node *filter, struct pico_frame *f);
|
||||
};
|
||||
|
||||
|
||||
static const struct fp_function fp_function[FILTER_COUNT] =
|
||||
{
|
||||
{&fp_priority},
|
||||
{&fp_reject},
|
||||
{&fp_drop}
|
||||
};
|
||||
|
||||
static int pico_ipv4_filter_add_validate(int8_t priority, enum filter_action action)
|
||||
{
|
||||
if ( priority > MAX_PRIORITY || priority < MIN_PRIORITY) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (action >= FILTER_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**************** FILTER API's ****************/
|
||||
uint32_t pico_ipv4_filter_add(struct pico_device *dev, uint8_t proto,
|
||||
struct pico_ip4 *out_addr, struct pico_ip4 *out_addr_netmask,
|
||||
struct pico_ip4 *in_addr, struct pico_ip4 *in_addr_netmask,
|
||||
uint16_t out_port, uint16_t in_port, int8_t priority,
|
||||
uint8_t tos, enum filter_action action)
|
||||
{
|
||||
static uint32_t filter_id = 1u; /* 0 is a special value used in the binary-tree search for packets being processed */
|
||||
struct filter_node *new_filter;
|
||||
|
||||
if (pico_ipv4_filter_add_validate(priority, action) < 0) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_filter = PICO_ZALLOC(sizeof(struct filter_node));
|
||||
if (!new_filter) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_filter->fdev = dev;
|
||||
new_filter->proto = proto;
|
||||
new_filter->out_addr = (!out_addr) ? (0U) : (out_addr->addr);
|
||||
new_filter->out_addr_netmask = (!out_addr_netmask) ? (0U) : (out_addr_netmask->addr);
|
||||
new_filter->in_addr = (!in_addr) ? (0U) : (in_addr->addr);
|
||||
new_filter->in_addr_netmask = (!in_addr_netmask) ? (0U) : (in_addr_netmask->addr);
|
||||
new_filter->out_port = out_port;
|
||||
new_filter->in_port = in_port;
|
||||
new_filter->priority = priority;
|
||||
new_filter->tos = tos;
|
||||
new_filter->filter_id = filter_id++;
|
||||
new_filter->function_ptr = fp_function[action].fn;
|
||||
|
||||
if(pico_tree_insert(&filter_tree, new_filter))
|
||||
{
|
||||
PICO_FREE(new_filter);
|
||||
filter_id--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new_filter->filter_id;
|
||||
}
|
||||
|
||||
int pico_ipv4_filter_del(uint32_t filter_id)
|
||||
{
|
||||
struct filter_node *node = NULL;
|
||||
struct filter_node dummy = {
|
||||
0
|
||||
};
|
||||
|
||||
dummy.filter_id = filter_id;
|
||||
if((node = pico_tree_delete(&filter_tree, &dummy)) == NULL)
|
||||
{
|
||||
ipf_dbg("ipfilter> failed to delete filter :%d\n", filter_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PICO_FREE(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipfilter_apply_filter(struct pico_frame *f, struct filter_node *pkt)
|
||||
{
|
||||
struct filter_node *filter_frame = NULL;
|
||||
filter_frame = pico_tree_findKey(&filter_tree, pkt);
|
||||
if(filter_frame)
|
||||
{
|
||||
filter_frame->function_ptr(filter_frame, f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipfilter(struct pico_frame *f)
|
||||
{
|
||||
struct filter_node temp;
|
||||
struct pico_ipv4_hdr *ipv4_hdr = (struct pico_ipv4_hdr *) f->net_hdr;
|
||||
struct pico_trans *trans;
|
||||
struct pico_icmp4_hdr *icmp_hdr;
|
||||
|
||||
memset(&temp, 0u, sizeof(struct filter_node));
|
||||
|
||||
temp.fdev = f->dev;
|
||||
temp.out_addr = ipv4_hdr->dst.addr;
|
||||
temp.in_addr = ipv4_hdr->src.addr;
|
||||
if ((ipv4_hdr->proto == PICO_PROTO_TCP) || (ipv4_hdr->proto == PICO_PROTO_UDP)) {
|
||||
trans = (struct pico_trans *) f->transport_hdr;
|
||||
temp.out_port = short_be(trans->dport);
|
||||
temp.in_port = short_be(trans->sport);
|
||||
}
|
||||
else if(ipv4_hdr->proto == PICO_PROTO_ICMP4) {
|
||||
icmp_hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
|
||||
if(icmp_hdr->type == PICO_ICMP_UNREACH && icmp_hdr->code == PICO_ICMP_UNREACH_FILTER_PROHIB)
|
||||
return 0;
|
||||
}
|
||||
|
||||
temp.proto = ipv4_hdr->proto;
|
||||
temp.priority = f->priority;
|
||||
temp.tos = ipv4_hdr->tos;
|
||||
return ipfilter_apply_filter(f, &temp);
|
||||
}
|
||||
|
||||
29
kernel/picotcp/modules/pico_ipfilter.h
Normal file
29
kernel/picotcp/modules/pico_ipfilter.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Simon Maes
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_IPFILTER
|
||||
#define INCLUDE_PICO_IPFILTER
|
||||
|
||||
#include "pico_device.h"
|
||||
|
||||
enum filter_action {
|
||||
FILTER_PRIORITY = 0,
|
||||
FILTER_REJECT,
|
||||
FILTER_DROP,
|
||||
FILTER_COUNT
|
||||
};
|
||||
|
||||
uint32_t pico_ipv4_filter_add(struct pico_device *dev, uint8_t proto,
|
||||
struct pico_ip4 *out_addr, struct pico_ip4 *out_addr_netmask, struct pico_ip4 *in_addr,
|
||||
struct pico_ip4 *in_addr_netmask, uint16_t out_port, uint16_t in_port,
|
||||
int8_t priority, uint8_t tos, enum filter_action action);
|
||||
|
||||
int pico_ipv4_filter_del(uint32_t filter_id);
|
||||
|
||||
int ipfilter(struct pico_frame *f);
|
||||
|
||||
#endif /* _INCLUDE_PICO_IPFILTER */
|
||||
|
||||
1658
kernel/picotcp/modules/pico_ipv4.c
Normal file
1658
kernel/picotcp/modules/pico_ipv4.c
Normal file
File diff suppressed because it is too large
Load Diff
115
kernel/picotcp/modules/pico_ipv4.h
Normal file
115
kernel/picotcp/modules/pico_ipv4.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_IPV4
|
||||
#define INCLUDE_PICO_IPV4
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_tree.h"
|
||||
|
||||
#define PICO_IPV4_INADDR_ANY 0x00000000U
|
||||
|
||||
#define PICO_IPV4_MTU (1500u)
|
||||
#define PICO_SIZE_IP4HDR (uint32_t)((sizeof(struct pico_ipv4_hdr)))
|
||||
#define PICO_IPV4_MAXPAYLOAD (PICO_IPV4_MTU - PICO_SIZE_IP4HDR)
|
||||
#define PICO_IPV4_DONTFRAG 0x4000U
|
||||
#define PICO_IPV4_MOREFRAG 0x2000U
|
||||
#define PICO_IPV4_EVIL 0x8000U
|
||||
#define PICO_IPV4_FRAG_MASK 0x1FFFU
|
||||
#define PICO_IPV4_DEFAULT_TTL 64
|
||||
#ifndef MBED
|
||||
#define PICO_IPV4_FRAG_MAX_SIZE (uint32_t)(63 * 1024)
|
||||
#else
|
||||
#define PICO_IPV4_FRAG_MAX_SIZE PICO_DEFAULT_SOCKETQ
|
||||
#endif
|
||||
|
||||
extern struct pico_protocol pico_proto_ipv4;
|
||||
|
||||
PACKED_STRUCT_DEF pico_ipv4_hdr {
|
||||
uint8_t vhl;
|
||||
uint8_t tos;
|
||||
uint16_t len;
|
||||
uint16_t id;
|
||||
uint16_t frag;
|
||||
uint8_t ttl;
|
||||
uint8_t proto;
|
||||
uint16_t crc;
|
||||
struct pico_ip4 src;
|
||||
struct pico_ip4 dst;
|
||||
uint8_t options[];
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_ipv4_pseudo_hdr
|
||||
{
|
||||
struct pico_ip4 src;
|
||||
struct pico_ip4 dst;
|
||||
uint8_t zeros;
|
||||
uint8_t proto;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
/* Interface: link to device */
|
||||
struct pico_mcast_list;
|
||||
|
||||
struct pico_ipv4_link
|
||||
{
|
||||
struct pico_device *dev;
|
||||
struct pico_ip4 address;
|
||||
struct pico_ip4 netmask;
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
struct pico_tree *MCASTGroups;
|
||||
uint8_t mcast_compatibility;
|
||||
uint8_t mcast_last_query_interval;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct pico_ipv4_route
|
||||
{
|
||||
struct pico_ip4 dest;
|
||||
struct pico_ip4 netmask;
|
||||
struct pico_ip4 gateway;
|
||||
struct pico_ipv4_link *link;
|
||||
uint32_t metric;
|
||||
};
|
||||
|
||||
extern struct pico_tree Routes;
|
||||
|
||||
|
||||
int pico_ipv4_compare(struct pico_ip4 *a, struct pico_ip4 *b);
|
||||
int pico_ipv4_to_string(char *ipbuf, const uint32_t ip);
|
||||
int pico_string_to_ipv4(const char *ipstr, uint32_t *ip);
|
||||
int pico_ipv4_valid_netmask(uint32_t mask);
|
||||
int pico_ipv4_is_unicast(uint32_t address);
|
||||
int pico_ipv4_is_multicast(uint32_t address);
|
||||
int pico_ipv4_is_broadcast(uint32_t addr);
|
||||
int pico_ipv4_is_loopback(uint32_t addr);
|
||||
int pico_ipv4_is_valid_src(uint32_t addr, struct pico_device *dev);
|
||||
|
||||
int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask);
|
||||
int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address);
|
||||
int pico_ipv4_rebound(struct pico_frame *f);
|
||||
|
||||
int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto);
|
||||
struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address);
|
||||
struct pico_ipv4_link *pico_ipv4_link_by_dev(struct pico_device *dev);
|
||||
struct pico_ipv4_link *pico_ipv4_link_by_dev_next(struct pico_device *dev, struct pico_ipv4_link *last);
|
||||
struct pico_device *pico_ipv4_link_find(struct pico_ip4 *address);
|
||||
struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst);
|
||||
struct pico_device *pico_ipv4_source_dev_find(const struct pico_ip4 *dst);
|
||||
int pico_ipv4_route_add(struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link);
|
||||
int pico_ipv4_route_del(struct pico_ip4 address, struct pico_ip4 netmask, int metric);
|
||||
struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr);
|
||||
void pico_ipv4_route_set_bcast_link(struct pico_ipv4_link *link);
|
||||
void pico_ipv4_unreachable(struct pico_frame *f, int err);
|
||||
|
||||
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);
|
||||
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_ipv4_link *pico_ipv4_get_default_mcastlink(void);
|
||||
int pico_ipv4_cleanup_links(struct pico_device *dev);
|
||||
|
||||
#endif /* _INCLUDE_PICO_IPV4 */
|
||||
2140
kernel/picotcp/modules/pico_ipv6.c
Normal file
2140
kernel/picotcp/modules/pico_ipv6.c
Normal file
File diff suppressed because it is too large
Load Diff
182
kernel/picotcp/modules/pico_ipv6.h
Normal file
182
kernel/picotcp/modules/pico_ipv6.h
Normal file
@ -0,0 +1,182 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef _INCLUDE_PICO_IPV6
|
||||
#define _INCLUDE_PICO_IPV6
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_ipv4.h"
|
||||
|
||||
#define PICO_SIZE_IP6HDR ((uint32_t)(sizeof(struct pico_ipv6_hdr)))
|
||||
#define PICO_IPV6_DEFAULT_HOP 64
|
||||
#define PICO_IPV6_MIN_MTU 1280
|
||||
#define PICO_IPV6_STRING 46
|
||||
|
||||
#define PICO_IPV6_EXTHDR_HOPBYHOP 0
|
||||
#define PICO_IPV6_EXTHDR_ROUTING 43
|
||||
#define PICO_IPV6_EXTHDR_FRAG 44
|
||||
#define PICO_IPV6_EXTHDR_ESP 50
|
||||
#define PICO_IPV6_EXTHDR_AUTH 51
|
||||
#define PICO_IPV6_EXTHDR_NONE 59
|
||||
#define PICO_IPV6_EXTHDR_DESTOPT 60
|
||||
|
||||
#define PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT 5
|
||||
#define PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT_DATALEN 2
|
||||
|
||||
#define HBH_LEN(hbh) ((((hbh->ext.hopbyhop.len + 1) << 3) - 2)) /* len in bytes, minus nxthdr and len byte */
|
||||
#define IPV6_OPTLEN(x) ((uint16_t)(((x + 1) << 3)))
|
||||
|
||||
extern const uint8_t PICO_IP6_ANY[PICO_SIZE_IP6];
|
||||
extern struct pico_protocol pico_proto_ipv6;
|
||||
extern struct pico_tree IPV6Routes;
|
||||
|
||||
PACKED_STRUCT_DEF pico_ipv6_hdr {
|
||||
uint32_t vtf;
|
||||
uint16_t len;
|
||||
uint8_t nxthdr;
|
||||
uint8_t hop;
|
||||
struct pico_ip6 src;
|
||||
struct pico_ip6 dst;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_ipv6_pseudo_hdr
|
||||
{
|
||||
struct pico_ip6 src;
|
||||
struct pico_ip6 dst;
|
||||
uint32_t len;
|
||||
uint8_t zero[3];
|
||||
uint8_t nxthdr;
|
||||
};
|
||||
|
||||
struct pico_ipv6_link
|
||||
{
|
||||
struct pico_device *dev;
|
||||
struct pico_ip6 address;
|
||||
struct pico_ip6 netmask;
|
||||
uint8_t istentative : 1;
|
||||
uint8_t isduplicate : 1;
|
||||
uint32_t dad_timer;
|
||||
uint16_t dup_detect_retrans;
|
||||
uint8_t retrans;
|
||||
pico_time expire_time;
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
struct pico_tree *MCASTGroups;
|
||||
uint8_t mcast_compatibility;
|
||||
uint8_t mcast_last_query_interval;
|
||||
#endif
|
||||
};
|
||||
|
||||
union pico_link {
|
||||
struct pico_ipv4_link ipv4;
|
||||
struct pico_ipv6_link ipv6;
|
||||
};
|
||||
|
||||
struct pico_ipv6_hbhoption {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
};
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
struct pico_ipv6_mcast_group {
|
||||
uint8_t filter_mode;
|
||||
uint16_t reference_count;
|
||||
struct pico_ip6 mcast_addr;
|
||||
struct pico_tree MCASTSources;
|
||||
};
|
||||
#endif
|
||||
struct pico_ipv6_destoption {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
};
|
||||
|
||||
struct pico_ipv6_route
|
||||
{
|
||||
struct pico_ip6 dest;
|
||||
struct pico_ip6 netmask;
|
||||
struct pico_ip6 gateway;
|
||||
pico_time backoff;
|
||||
uint8_t retrans;
|
||||
struct pico_ipv6_link *link;
|
||||
uint32_t metric;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_ipv6_exthdr {
|
||||
uint8_t nxthdr;
|
||||
|
||||
PACKED_UNION_DEF ipv6_ext_u {
|
||||
PEDANTIC_STRUCT_DEF hopbyhop_s {
|
||||
uint8_t len;
|
||||
} hopbyhop;
|
||||
|
||||
PEDANTIC_STRUCT_DEF destopt_s {
|
||||
uint8_t len;
|
||||
} destopt;
|
||||
|
||||
PEDANTIC_STRUCT_DEF routing_s {
|
||||
uint8_t len;
|
||||
uint8_t routtype;
|
||||
uint8_t segleft;
|
||||
} routing;
|
||||
|
||||
PEDANTIC_STRUCT_DEF fragmentation_s {
|
||||
uint8_t res;
|
||||
uint8_t om[2];
|
||||
uint8_t id[4];
|
||||
} frag;
|
||||
} ext;
|
||||
};
|
||||
|
||||
int pico_ipv6_compare(struct pico_ip6 *a, struct pico_ip6 *b);
|
||||
int pico_string_to_ipv6(const char *ipstr, uint8_t *ip);
|
||||
int pico_ipv6_to_string(char *ipbuf, const uint8_t ip[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_unicast(struct pico_ip6 *a);
|
||||
int pico_ipv6_is_multicast(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_allhosts_multicast(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_solnode_multicast(const uint8_t addr[PICO_SIZE_IP6], struct pico_device *dev);
|
||||
int pico_ipv6_is_global(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_uniquelocal(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_sitelocal(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_linklocal(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_solicited(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_unspecified(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
int pico_ipv6_is_localhost(const uint8_t addr[PICO_SIZE_IP6]);
|
||||
|
||||
int pico_ipv6_frame_push(struct pico_frame *f, struct pico_ip6 *src, struct pico_ip6 *dst, uint8_t proto, int is_dad);
|
||||
int pico_ipv6_route_add(struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link);
|
||||
int pico_ipv6_route_del(struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link);
|
||||
void pico_ipv6_unreachable(struct pico_frame *f, uint8_t code);
|
||||
|
||||
struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask);
|
||||
struct pico_ipv6_link *pico_ipv6_link_add_no_dad(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask);
|
||||
int pico_ipv6_link_del(struct pico_device *dev, struct pico_ip6 address);
|
||||
int pico_ipv6_cleanup_links(struct pico_device *dev);
|
||||
struct pico_ipv6_link *pico_ipv6_link_istentative(struct pico_ip6 *address);
|
||||
struct pico_ipv6_link *pico_ipv6_link_get(struct pico_ip6 *address);
|
||||
struct pico_device *pico_ipv6_link_find(struct pico_ip6 *address);
|
||||
struct pico_ip6 pico_ipv6_route_get_gateway(struct pico_ip6 *addr);
|
||||
struct pico_ip6 *pico_ipv6_source_find(const struct pico_ip6 *dst);
|
||||
struct pico_device *pico_ipv6_source_dev_find(const struct pico_ip6 *dst);
|
||||
struct pico_ipv6_link *pico_ipv6_link_by_dev(struct pico_device *dev);
|
||||
struct pico_ipv6_link *pico_ipv6_link_by_dev_next(struct pico_device *dev, struct pico_ipv6_link *last);
|
||||
struct pico_ipv6_link *pico_ipv6_global_get(struct pico_device *dev);
|
||||
struct pico_ipv6_link *pico_ipv6_linklocal_get(struct pico_device *dev);
|
||||
struct pico_ipv6_link *pico_ipv6_sitelocal_get(struct pico_device *dev);
|
||||
struct pico_ipv6_link *pico_ipv6_prefix_configured(struct pico_ip6 *prefix);
|
||||
struct pico_ipv6_route *pico_ipv6_gateway_by_dev(struct pico_device *dev);
|
||||
struct pico_ipv6_route *pico_ipv6_gateway_by_dev_next(struct pico_device *dev, struct pico_ipv6_route *last);
|
||||
int pico_ipv6_lifetime_set(struct pico_ipv6_link *l, pico_time expire);
|
||||
void pico_ipv6_check_lifetime_expired(pico_time now, void *arg);
|
||||
int pico_ipv6_dev_routing_enable(struct pico_device *dev);
|
||||
int pico_ipv6_dev_routing_disable(struct pico_device *dev);
|
||||
void pico_ipv6_router_down(struct pico_ip6 *address);
|
||||
|
||||
int pico_ipv6_mcast_join(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *_MCASTFilter);
|
||||
int pico_ipv6_mcast_leave(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *_MCASTFilter);
|
||||
|
||||
struct pico_ipv6_link *pico_ipv6_get_default_mcastlink(void);
|
||||
|
||||
int pico_ipv6_is_null_address(struct pico_ip6 *ip6);
|
||||
#endif
|
||||
1586
kernel/picotcp/modules/pico_ipv6_nd.c
Normal file
1586
kernel/picotcp/modules/pico_ipv6_nd.c
Normal file
File diff suppressed because it is too large
Load Diff
36
kernel/picotcp/modules/pico_ipv6_nd.h
Normal file
36
kernel/picotcp/modules/pico_ipv6_nd.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef _INCLUDE_PICO_ND
|
||||
#define _INCLUDE_PICO_ND
|
||||
#include "pico_frame.h"
|
||||
#include "pico_ipv6.h"
|
||||
|
||||
/* RFC constants */
|
||||
#define PICO_ND_REACHABLE_TIME 30000 /* msec */
|
||||
#define PICO_ND_RETRANS_TIMER 1000 /* msec */
|
||||
|
||||
struct pico_nd_hostvars {
|
||||
uint8_t routing;
|
||||
uint8_t hoplimit;
|
||||
pico_time basetime;
|
||||
pico_time reachabletime;
|
||||
pico_time retranstime;
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
uint8_t lowpan_flags;
|
||||
#endif
|
||||
};
|
||||
|
||||
void pico_ipv6_nd_init(void);
|
||||
struct pico_eth *pico_ipv6_get_neighbor(struct pico_frame *f);
|
||||
void pico_ipv6_nd_postpone(struct pico_frame *f);
|
||||
int pico_ipv6_nd_recv(struct pico_frame *f);
|
||||
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
int pico_6lp_nd_start_soliciting(struct pico_ipv6_link *l, struct pico_ipv6_route *gw);
|
||||
void pico_6lp_nd_register(struct pico_ipv6_link *link);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
259
kernel/picotcp/modules/pico_mcast.c
Normal file
259
kernel/picotcp/modules/pico_mcast.c
Normal file
@ -0,0 +1,259 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
This module handles the equalities between the IGMP and the MLD protocol
|
||||
Authors: Roel Postelmans
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_stack.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_mld.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_eth.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_frame.h"
|
||||
#include "pico_tree.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_icmp6.h"
|
||||
#include "pico_dns_client.h"
|
||||
#include "pico_mld.h"
|
||||
#include "pico_igmp.h"
|
||||
#include "pico_constants.h"
|
||||
#include "pico_mcast.h"
|
||||
|
||||
#if (((defined(PICO_SUPPORT_MLD) && defined(PICO_SUPPORT_IPV6)) || defined(PICO_SUPPORT_IGMP)) && defined(PICO_SUPPORT_MCAST))
|
||||
|
||||
#ifdef DEBUG_MCAST
|
||||
#define multicast_dbg dbg
|
||||
#else
|
||||
#define multicast_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define MCAST_EVENT_DELETE_GROUP (0x0)
|
||||
#define MCAST_EVENT_CREATE_GROUP (0x1)
|
||||
#define MCAST_EVENT_UPDATE_GROUP (0x2)
|
||||
#define MCAST_EVENT_QUERY_RECV (0x3)
|
||||
#define MCAST_EVENT_REPORT_RECV (0x4)
|
||||
#define MCAST_EVENT_TIMER_EXPIRED (0x5)
|
||||
|
||||
#define MCAST_MODE_IS_INCLUDE (1)
|
||||
#define MCAST_MODE_IS_EXCLUDE (2)
|
||||
#define MCAST_CHANGE_TO_INCLUDE_MODE (3)
|
||||
#define MCAST_CHANGE_TO_EXCLUDE_MODE (4)
|
||||
|
||||
#define MCAST_MODE_IS_INCLUDE (1)
|
||||
#define MCAST_MODE_IS_EXCLUDE (2)
|
||||
#define MCAST_CHANGE_TO_INCLUDE_MODE (3)
|
||||
#define MCAST_CHANGE_TO_EXCLUDE_MODE (4)
|
||||
#define MCAST_ALLOW_NEW_SOURCES (5)
|
||||
#define MCAST_BLOCK_OLD_SOURCES (6)
|
||||
|
||||
typedef int (*mcast_callback)(struct mcast_filter_parameters *);
|
||||
|
||||
static void pico_mcast_src_filtering_cleanup(struct mcast_filter_parameters*mcast )
|
||||
{
|
||||
struct pico_tree_node *index = NULL, *_tmp = NULL;
|
||||
/* cleanup filters */
|
||||
pico_tree_foreach_safe(index, mcast->allow, _tmp)
|
||||
{
|
||||
pico_tree_delete(mcast->allow, index->keyValue);
|
||||
}
|
||||
pico_tree_foreach_safe(index, mcast->block, _tmp)
|
||||
{
|
||||
pico_tree_delete(mcast->block, index->keyValue);
|
||||
}
|
||||
}
|
||||
static int pico_mcast_src_filtering_inc_inc(struct mcast_filter_parameters*mcast )
|
||||
{
|
||||
struct pico_tree_node *index = NULL;
|
||||
union pico_address *source;
|
||||
/* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
|
||||
if (mcast->p->event == MCAST_EVENT_DELETE_GROUP) {
|
||||
/* TO_IN (B) */
|
||||
mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
|
||||
mcast->filter = mcast->allow;
|
||||
if (mcast->p->MCASTFilter) {
|
||||
pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
|
||||
{
|
||||
if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
|
||||
multicast_dbg("MCAST: Failed to insert entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
mcast->sources++;
|
||||
}
|
||||
} /* else { allow stays empty } */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ALLOW (B-A) */
|
||||
/* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
|
||||
if (mcast->p->event == MCAST_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
|
||||
mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
|
||||
else
|
||||
mcast->record_type = MCAST_ALLOW_NEW_SOURCES;
|
||||
|
||||
mcast->filter = mcast->allow;
|
||||
pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
|
||||
{
|
||||
if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
|
||||
multicast_dbg("MCAST: Failed to insert entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
mcast->sources++;
|
||||
}
|
||||
pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
|
||||
{
|
||||
source = pico_tree_findKey(mcast->allow, index->keyValue);
|
||||
if (source) {
|
||||
pico_tree_delete(mcast->allow, source);
|
||||
mcast->sources--;
|
||||
}
|
||||
}
|
||||
if (!pico_tree_empty(mcast->allow)) /* record type is ALLOW */
|
||||
return 0;
|
||||
|
||||
/* BLOCK (A-B) */
|
||||
mcast->record_type = MCAST_BLOCK_OLD_SOURCES;
|
||||
mcast->filter = mcast->block;
|
||||
pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
|
||||
{
|
||||
if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
|
||||
multicast_dbg("MCAST: Failed to insert entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
mcast->sources++;
|
||||
}
|
||||
pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
|
||||
{
|
||||
source = pico_tree_findKey(mcast->block, index->keyValue);
|
||||
if (source) {
|
||||
pico_tree_delete(mcast->block, source);
|
||||
mcast->sources--;
|
||||
}
|
||||
}
|
||||
if (!pico_tree_empty(mcast->block)) /* record type is BLOCK */
|
||||
return 0;
|
||||
|
||||
/* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report */
|
||||
(mcast->p)->f = NULL;
|
||||
return MCAST_NO_REPORT;
|
||||
}
|
||||
|
||||
static int pico_mcast_src_filtering_inc_excl(struct mcast_filter_parameters*mcast )
|
||||
{
|
||||
struct pico_tree_node *index = NULL;
|
||||
mcast->record_type = MCAST_CHANGE_TO_EXCLUDE_MODE;
|
||||
mcast->filter = mcast->block;
|
||||
pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
|
||||
{
|
||||
if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
|
||||
multicast_dbg("MCAST: Failed to insert entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
mcast->sources++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int pico_mcast_src_filtering_excl_inc(struct mcast_filter_parameters*mcast )
|
||||
{
|
||||
struct pico_tree_node *index = NULL;
|
||||
mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
|
||||
mcast->filter = mcast->allow;
|
||||
if (mcast->p->MCASTFilter) {
|
||||
pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
|
||||
{
|
||||
if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
|
||||
multicast_dbg("MCAST: Failed to insert entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
mcast->sources++;
|
||||
}
|
||||
} /* else { allow stays empty } */
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int pico_mcast_src_filtering_excl_excl(struct mcast_filter_parameters*mcast )
|
||||
{
|
||||
struct pico_tree_node *index = NULL;
|
||||
struct pico_ip6 *source = NULL;
|
||||
mcast->record_type = MCAST_BLOCK_OLD_SOURCES;
|
||||
mcast->filter = mcast->block;
|
||||
pico_tree_foreach(index, mcast->p->MCASTFilter)
|
||||
{
|
||||
if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
|
||||
multicast_dbg("MCAST: Failed to insert entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mcast->sources++;
|
||||
}
|
||||
pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
|
||||
{
|
||||
source = pico_tree_findKey(mcast->block, index->keyValue); /* B */
|
||||
if (source) {
|
||||
pico_tree_delete(mcast->block, source);
|
||||
mcast->sources--;
|
||||
}
|
||||
}
|
||||
if (!pico_tree_empty(mcast->block)) /* record type is BLOCK */
|
||||
return 0;
|
||||
|
||||
/* ALLOW (A-B) */
|
||||
mcast->record_type = MCAST_ALLOW_NEW_SOURCES;
|
||||
mcast->filter = mcast->allow;
|
||||
pico_tree_foreach(index, &mcast->g->MCASTSources)
|
||||
{
|
||||
if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
|
||||
multicast_dbg("MCAST: Failed to insert entry in tree\n");
|
||||
return -1;
|
||||
}
|
||||
mcast->sources++;
|
||||
}
|
||||
pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
|
||||
{
|
||||
source = pico_tree_findKey(mcast->allow, index->keyValue); /* A */
|
||||
if (source) {
|
||||
pico_tree_delete(mcast->allow, source);
|
||||
mcast->sources--;
|
||||
}
|
||||
}
|
||||
if (!pico_tree_empty(mcast->allow)) /* record type is ALLOW */
|
||||
return 0;
|
||||
|
||||
/* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report */
|
||||
mcast->p->f = NULL;
|
||||
return MCAST_NO_REPORT;
|
||||
}
|
||||
static const mcast_callback mcast_filter_state[2][2] =
|
||||
{
|
||||
{ pico_mcast_src_filtering_excl_excl, pico_mcast_src_filtering_excl_inc},
|
||||
{ pico_mcast_src_filtering_inc_excl, pico_mcast_src_filtering_inc_inc }
|
||||
};
|
||||
int8_t pico_mcast_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
|
||||
{
|
||||
int ret = -1;
|
||||
/* "non-existent" state of filter mode INCLUDE and empty source list */
|
||||
if (p->event == MCAST_EVENT_DELETE_GROUP) {
|
||||
p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
|
||||
p->MCASTFilter = NULL;
|
||||
}
|
||||
|
||||
if (p->event == MCAST_EVENT_QUERY_RECV)
|
||||
return 0;
|
||||
|
||||
pico_mcast_src_filtering_cleanup(filter);
|
||||
|
||||
if(filter->g->filter_mode <= PICO_IP_MULTICAST_INCLUDE )
|
||||
{
|
||||
if(p->filter_mode <= PICO_IP_MULTICAST_INCLUDE)
|
||||
{
|
||||
ret = mcast_filter_state[filter->g->filter_mode][p->filter_mode](filter);
|
||||
}
|
||||
}
|
||||
|
||||
return (int8_t) ret;
|
||||
}
|
||||
#endif
|
||||
53
kernel/picotcp/modules/pico_mcast.h
Normal file
53
kernel/picotcp/modules/pico_mcast.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef INCLUDE_PICO_MCAST
|
||||
#define INCLUDE_PICO_MCAST
|
||||
|
||||
#define MCAST_MODE_IS_INCLUDE (1)
|
||||
#define MCAST_MODE_IS_EXCLUDE (2)
|
||||
#define MCAST_CHANGE_TO_INCLUDE_MODE (3)
|
||||
#define MCAST_CHANGE_TO_EXCLUDE_MODE (4)
|
||||
#define MCAST_ALLOW_NEW_SOURCES (5)
|
||||
#define MCAST_BLOCK_OLD_SOURCES (6)
|
||||
#define MCAST_EVENT_DELETE_GROUP (0x0)
|
||||
#define MCAST_EVENT_CREATE_GROUP (0x1)
|
||||
#define MCAST_EVENT_UPDATE_GROUP (0x2)
|
||||
#define MCAST_EVENT_QUERY_RECV (0x3)
|
||||
#define MCAST_EVENT_REPORT_RECV (0x4)
|
||||
#define MCAST_EVENT_TIMER_EXPIRED (0x5)
|
||||
#define MCAST_NO_REPORT (1)
|
||||
|
||||
PACKED_STRUCT_DEF mcast_parameters {
|
||||
uint8_t event;
|
||||
uint8_t state;
|
||||
uint8_t general_query;
|
||||
uint8_t filter_mode;
|
||||
uint8_t last_host;
|
||||
uint16_t max_resp_time;
|
||||
union pico_address mcast_link;
|
||||
union pico_address mcast_group;
|
||||
struct pico_tree *MCASTFilter;
|
||||
struct pico_frame *f;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_mcast_group {
|
||||
uint8_t filter_mode;
|
||||
uint16_t reference_count;
|
||||
union pico_address mcast_addr;
|
||||
struct pico_tree MCASTSources;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF mcast_filter_parameters {
|
||||
struct mcast_parameters *p;
|
||||
struct pico_tree *allow;
|
||||
struct pico_tree *block;
|
||||
struct pico_tree *filter;
|
||||
uint16_t sources;
|
||||
uint8_t proto;
|
||||
uint8_t record_type;
|
||||
struct pico_mcast_group *g;
|
||||
union pico_link *link;
|
||||
};
|
||||
|
||||
|
||||
extern int8_t pico_mcast_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p);
|
||||
|
||||
#endif
|
||||
3687
kernel/picotcp/modules/pico_mdns.c
Normal file
3687
kernel/picotcp/modules/pico_mdns.c
Normal file
File diff suppressed because it is too large
Load Diff
206
kernel/picotcp/modules/pico_mdns.h
Normal file
206
kernel/picotcp/modules/pico_mdns.h
Normal file
@ -0,0 +1,206 @@
|
||||
/* ****************************************************************************
|
||||
* PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved.
|
||||
* See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
* .
|
||||
* Author: Toon Stegen, Jelle De Vleeschouwer
|
||||
* ****************************************************************************/
|
||||
#ifndef INCLUDE_PICO_MDNS
|
||||
#define INCLUDE_PICO_MDNS
|
||||
|
||||
#include "pico_dns_common.h"
|
||||
#include "pico_tree.h"
|
||||
#include "pico_ipv4.h"
|
||||
|
||||
/* ********************************* CONFIG ***********************************/
|
||||
#define PICO_MDNS_PROBE_UNICAST 1 /* Probe queries as QU-questions */
|
||||
#define PICO_MDNS_CONTINUOUS_REFRESH 0 /* Continuously update cache */
|
||||
#define PICO_MDNS_ALLOW_CACHING 1 /* Enable caching on this host */
|
||||
#define PICO_MDNS_DEFAULT_TTL 120 /* Default TTL of mDNS records */
|
||||
#define PICO_MDNS_SERVICE_TTL 120 /* Default TTL of SRV/TXT/PTR/NSEC */
|
||||
#define PICO_MDNS_PROBE_COUNT 3
|
||||
/* Amount of probes to send:
|
||||
RFC6762: 8.1. Probing:
|
||||
250 ms after the first query, the host should send a second; then,
|
||||
250 ms after that, a third. If, by 250 ms after the third probe, no
|
||||
conflicting Multicast DNS responses have been received, the host may
|
||||
move to the next step, announcing.
|
||||
*/
|
||||
|
||||
#define PICO_MDNS_ANNOUNCEMENT_COUNT 3
|
||||
/* Amount of announcements to send: (we've opted for 1 extra for robustness)
|
||||
RFC6762: 8.3. Announcing:
|
||||
The Multicast DNS responder MUST send at least two unsolicited
|
||||
responses, one second apart. To provide increased robustness against
|
||||
packet loss, a responder MAY send up to eight unsolicited responses,
|
||||
provided that the interval between unsolicited responses increases by
|
||||
at least a factor of two with every response sent.
|
||||
*/
|
||||
/* ****************************************************************************/
|
||||
|
||||
#define PICO_MDNS_DEST_ADDR4 "224.0.0.251"
|
||||
|
||||
/* To make mDNS records unique or shared records */
|
||||
#define PICO_MDNS_RECORD_UNIQUE 0x00u
|
||||
#define PICO_MDNS_RECORD_SHARED 0x01u
|
||||
|
||||
/* To indicate if we reclaim or not */
|
||||
#define PICO_MDNS_RECLAIM 1
|
||||
#define PICO_MDNS_NO_RECLAIM 0
|
||||
|
||||
/* Flag to check for when records are returned, to determine the hostname */
|
||||
#define PICO_MDNS_RECORD_HOSTNAME 0x02u
|
||||
#define IS_HOSTNAME_RECORD(x) \
|
||||
(((x)->flags) & PICO_MDNS_RECORD_HOSTNAME) ? (1) : (0)
|
||||
|
||||
/* --- MDNS resource record --- */
|
||||
struct pico_mdns_record
|
||||
{
|
||||
struct pico_dns_record *record; /* DNS Resource Record */
|
||||
uint32_t current_ttl; /* Current TTL */
|
||||
uint8_t flags; /* Resource Record flags */
|
||||
uint8_t claim_id; /* Claim ID number */
|
||||
};
|
||||
|
||||
/* ****************************************************************************
|
||||
* Compares 2 mDNS records by type, name AND rdata for a truly unique result
|
||||
*
|
||||
* @param ra mDNS record A
|
||||
* @param rb mDNS record B
|
||||
* @return 0 when records are equal, returns difference when they're not.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_mdns_record_cmp( void *a, void *b );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Deletes a single mDNS resource record.
|
||||
*
|
||||
* @param record Void-pointer to mDNS Resource Record. Can be used with pico_-
|
||||
* tree-destroy.
|
||||
* @return Returns 0 on success, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_mdns_record_delete( void **record );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Creates a single standalone mDNS resource record with given name, type and
|
||||
* data to register on the network.
|
||||
*
|
||||
* @param url DNS rrecord name in URL format. Will be converted to DNS
|
||||
* name notation format.
|
||||
* @param _rdata Memory buffer with data to insert in the resource record. If
|
||||
* data of record should contain a DNS name, the name in the
|
||||
* databuffer needs to be in URL-format.
|
||||
* @param datalen The exact length in bytes of the _rdata-buffer. If data of
|
||||
* record should contain a DNS name, datalen needs to be
|
||||
* pico_dns_strlen(_rdata).
|
||||
* @param rtype DNS type of the resource record to be.
|
||||
* @param rclass DNS class of the resource record to be.
|
||||
* @param rttl DNS ttl of the resource record to be.
|
||||
* @param flags You can specify if the mDNS record should be a shared record
|
||||
* rather than a unique record.
|
||||
* @return Pointer to newly created mDNS resource record.
|
||||
* ****************************************************************************/
|
||||
struct pico_mdns_record *
|
||||
pico_mdns_record_create( const char *url,
|
||||
void *_rdata,
|
||||
uint16_t datalen,
|
||||
uint16_t rtype,
|
||||
uint32_t rttl,
|
||||
uint8_t flags );
|
||||
|
||||
|
||||
|
||||
/* ****************************************************************************
|
||||
* Definition of DNS record tree
|
||||
* ****************************************************************************/
|
||||
typedef struct pico_tree pico_mdns_rtree;
|
||||
#define PICO_MDNS_RTREE_DECLARE(name) \
|
||||
pico_mdns_rtree (name) = {&LEAF, pico_mdns_record_cmp}
|
||||
#define PICO_MDNS_RTREE_DESTROY(rtree) \
|
||||
pico_tree_destroy((rtree), pico_mdns_record_delete)
|
||||
#define PICO_MDNS_RTREE_ADD(tree, record) \
|
||||
pico_tree_insert((tree), (record))
|
||||
|
||||
/* ****************************************************************************
|
||||
* API-call to query a record with a certain URL and type. First checks the
|
||||
* Cache for this record. If no cache-entry is found, a query will be sent on
|
||||
* the wire for this record.
|
||||
*
|
||||
* @param url URL to query for.
|
||||
* @param type DNS type top query for.
|
||||
* @param callback Callback to call when records are found for the query.
|
||||
* @return 0 when query is correctly parsed, something else on failure.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_mdns_getrecord( const char *url, uint16_t type,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Claim all different mDNS records in a tree in a single API-call. All records
|
||||
* in tree are called in a single new claim-session.
|
||||
*
|
||||
* @param rtree mDNS record tree with records to claim
|
||||
* @param callback Callback to call when all record are properly claimed.
|
||||
* @return 0 When claiming didn't horribly fail.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_mdns_claim( pico_mdns_rtree record_tree,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Tries to claim a hostname for this machine. Claims automatically a
|
||||
* unique A record with the IPv4-address of this host.
|
||||
* The hostname won't be set directly when this functions returns,
|
||||
* but only if the claiming of the unique record succeeded.
|
||||
* Init-callback will be called when the hostname-record is successfully
|
||||
* registered.
|
||||
*
|
||||
* @param url URL to set the hostname to.
|
||||
* @param arg Argument to pass to the init-callback.
|
||||
* @return 0 when the host started registering the hostname-record successfully,
|
||||
* Returns something else when it didn't succeeded.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_mdns_tryclaim_hostname( const char *url, void *arg );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Get the current hostname for this machine.
|
||||
*
|
||||
* @return Returns the hostname for this machine when the module is initialised
|
||||
* Returns NULL when the module is not initialised.
|
||||
* ****************************************************************************/
|
||||
const char *
|
||||
pico_mdns_get_hostname( void );
|
||||
|
||||
/* ****************************************************************************
|
||||
* Initialises the entire mDNS-module and sets the hostname for this machine.
|
||||
* Sets up the global mDNS socket properly and calls callback when succeeded.
|
||||
* Only when the module is properly initialised records can be registered on
|
||||
* the module.
|
||||
*
|
||||
* @param hostname URL to set the hostname to.
|
||||
* @param address IPv4-address of this host to bind to.
|
||||
* @param callback Callback to call when the hostname is registered and
|
||||
* also the global mDNS module callback. Gets called when
|
||||
* Passive conflicts occur, so changes in records can be
|
||||
* tracked in this callback.
|
||||
* @param arg Argument to pass to the init-callback.
|
||||
* @return 0 when the module is properly initialised and the host started regis-
|
||||
* tering the hostname. Returns something else went the host failed
|
||||
* initialising the module or registering the hostname.
|
||||
* ****************************************************************************/
|
||||
int
|
||||
pico_mdns_init( const char *hostname,
|
||||
struct pico_ip4 address,
|
||||
void (*callback)(pico_mdns_rtree *,
|
||||
char *,
|
||||
void *),
|
||||
void *arg );
|
||||
|
||||
#endif /* _INCLUDE_PICO_MDNS */
|
||||
1165
kernel/picotcp/modules/pico_mld.c
Normal file
1165
kernel/picotcp/modules/pico_mld.c
Normal file
File diff suppressed because it is too large
Load Diff
119
kernel/picotcp/modules/pico_mld.h
Normal file
119
kernel/picotcp/modules/pico_mld.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Roel Postelmans
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_MLD
|
||||
#define INCLUDE_PICO_MLD
|
||||
|
||||
#define PICO_MLDV1 1
|
||||
#define PICO_MLDV2 2
|
||||
|
||||
#define PICO_MLD_QUERY 130
|
||||
#define PICO_MLD_REPORT 131
|
||||
#define PICO_MLD_DONE 132
|
||||
#define PICO_MLD_REPORTV2 143
|
||||
|
||||
/*RFC 3810 $6.2 */
|
||||
#define MLD_HOP_LIMIT 1
|
||||
|
||||
/* states */
|
||||
#define MLD_STATE_NON_LISTENER (0x0)
|
||||
#define MLD_STATE_DELAYING_LISTENER (0x1)
|
||||
#define MLD_STATE_IDLE_LISTENER (0x2)
|
||||
|
||||
#define PICO_MLD_STATE_CREATE 1
|
||||
#define PICO_MLD_STATE_UPDATE 2
|
||||
#define PICO_MLD_STATE_DELETE 3
|
||||
/* group record types */
|
||||
#define MLD_MODE_IS_INCLUDE (1)
|
||||
#define MLD_MODE_IS_EXCLUDE (2)
|
||||
#define MLD_CHANGE_TO_INCLUDE_MODE (3)
|
||||
#define MLD_CHANGE_TO_EXCLUDE_MODE (4)
|
||||
#define MLD_ALLOW_NEW_SOURCES (5)
|
||||
#define MLD_BLOCK_OLD_SOURCES (6)
|
||||
/* events */
|
||||
|
||||
#define MLD_EVENT_START_LISTENING (0x1)
|
||||
#define MLD_EVENT_STOP_LISTENING (0x0)
|
||||
#define MLD_EVENT_QUERY_RECV (0x3)
|
||||
#define MLD_EVENT_REPORT_RECV (0x4)
|
||||
#define MLD_EVENT_TIMER_EXPIRED (0x5)
|
||||
/*Not needed?*/
|
||||
#define MLD_EVENT_DONE_RECV (0x1)
|
||||
|
||||
#define MLD_EVENT_DELETE_GROUP (0x0)
|
||||
#define MLD_EVENT_CREATE_GROUP (0x1)
|
||||
#define MLD_EVENT_UPDATE_GROUP (0x2)
|
||||
#define MLD_EVENT_QUERY_RECV (0x3)
|
||||
#define MLD_EVENT_REPORT_RECV (0x4)
|
||||
#define MLD_EVENT_TIMER_EXPIRED (0x5)
|
||||
/* (default) Variabels for times/counters */
|
||||
/* ALL IN SECONDS */
|
||||
#define MLD_ROBUSTNESS (2)
|
||||
#define MLD_QUERY_INTERVAL (125)
|
||||
#define MLD_QUERY_RESPONSE_INTERVAL (10)
|
||||
#define MLD_DEFAULT_MAX_RESPONSE_TIME (100)
|
||||
#define MLD_MULTICAST_LISTENER_INTERVAL (MLD_ROBUSTNESS * MLD_QUERY_INTERVAL) + MLD_QUERY_RESPONSE_INTERVAL
|
||||
#define MLD_OTHER_QUERIER_PRESENT_INTERVAL (MLD_ROBUSTNESS * MLD_QUERY_INTERVAL) + (0.5 * MLD_QUERY_RESPONSE_INTERVAL)
|
||||
#define MLD_STARTUP_QUERY_INTERVAL (0.25 * MLD_QUERY_INTERVAL)
|
||||
#define MLD_STARTUP_QUERY_COUNT MLD_ROBUSTNESS
|
||||
#define MLD_LAST_LISTENER_QUERY_INTERVAL 1
|
||||
#define MLD_LISTENER_QUERY_COUNT MLD_ROBUSTNESS
|
||||
#define MLD_UNSOLICITED_REPORT_INTERVAL 10
|
||||
|
||||
/* custom timers types */
|
||||
#define MLD_TIMER_GROUP_REPORT (1)
|
||||
#define MLD_TIMER_V1_QUERIER (2)
|
||||
#define MLD_TIMER_V2_QUERIER (2)
|
||||
|
||||
|
||||
/* Who has send the last report message */
|
||||
#define MLD_HOST_LAST (0x1)
|
||||
#define MLD_HOST_NOT_LAST (0x0)
|
||||
|
||||
|
||||
#define MLD_TIMER_STOPPED (1)
|
||||
#define MLD_MAX_SOURCES (89)
|
||||
extern struct pico_protocol pico_proto_mld;
|
||||
|
||||
struct mld_multicast_address_record {
|
||||
uint8_t type;
|
||||
uint8_t aux_len;
|
||||
uint16_t nbr_src;
|
||||
struct pico_ip6 multicast;
|
||||
struct pico_ip6 src[1];
|
||||
};
|
||||
|
||||
struct mld_parameters {
|
||||
uint8_t event;
|
||||
uint8_t state;
|
||||
uint8_t general_query;
|
||||
uint8_t filter_mode;
|
||||
uint8_t last_host;
|
||||
uint16_t max_resp_time;
|
||||
struct pico_ip6 mcast_link;
|
||||
struct pico_ip6 mcast_group;
|
||||
struct pico_tree *MCASTFilter;
|
||||
struct pico_frame *f;
|
||||
};
|
||||
|
||||
struct mld_timer {
|
||||
uint8_t type;
|
||||
uint8_t stopped;
|
||||
pico_time start;
|
||||
pico_time delay;
|
||||
struct pico_ip6 mcast_link;
|
||||
struct pico_ip6 mcast_group;
|
||||
struct pico_frame *f;
|
||||
void (*mld_callback)(struct mld_timer *t);
|
||||
};
|
||||
|
||||
uint16_t pico_mld_checksum(struct pico_frame *f);
|
||||
int pico_mld_process_in(struct pico_frame *f);
|
||||
int pico_mld_state_change(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group, uint8_t filter_mode, struct pico_tree *_MCASTFilter, uint8_t state);
|
||||
#endif /* _INCLUDE_PICO_MLD */
|
||||
1615
kernel/picotcp/modules/pico_mm.c
Normal file
1615
kernel/picotcp/modules/pico_mm.c
Normal file
File diff suppressed because it is too large
Load Diff
98
kernel/picotcp/modules/pico_mm.h
Normal file
98
kernel/picotcp/modules/pico_mm.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Gustav Janssens, Jonas Van Nieuwenberg, Sam Van Den Berge
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#ifndef _INCLUDE_PICO_MM
|
||||
#define _INCLUDE_PICO_MM
|
||||
|
||||
#include "pico_config.h"
|
||||
|
||||
/*
|
||||
* Memory init function, this will create a memory manager instance
|
||||
* A memory_manager page will be created, along with one page of memory
|
||||
* Memory can be asked for via the pico_mem_zalloc function
|
||||
* More memory will be allocated to the memory manager according to its needs
|
||||
* A maximum amount of memory of uint32_t memsize can be allocated
|
||||
*/
|
||||
void pico_mem_init(uint32_t memsize);
|
||||
/*
|
||||
* Memory deinit function, this will free all memory occupied by the current
|
||||
* memory manager instance.
|
||||
*/
|
||||
void pico_mem_deinit(void);
|
||||
/*
|
||||
* Zero-initialized malloc function, will reserve a memory segment of length uint32_t len
|
||||
* This memory will be quickly allocated in a slab of fixed size if possible
|
||||
* or less optimally in the heap for a small variable size
|
||||
* The fixed size of the slabs can be changed dynamically via a statistics engine
|
||||
*/
|
||||
void*pico_mem_zalloc(size_t len);
|
||||
/*
|
||||
* Free function, free a block of memory pointed to by ptr.
|
||||
* Unused memory is only returned to the system's control by pico_mem_cleanup
|
||||
*/
|
||||
void pico_mem_free(void*ptr);
|
||||
/*
|
||||
* This cleanup function will be provided by the memory manager
|
||||
* It can be called during processor downtime
|
||||
* This function will return unused pages to the system's control
|
||||
* Pages are unused if they no longer contain slabs or heap, and they have been idle for a longer time
|
||||
*/
|
||||
void pico_mem_cleanup(uint32_t timestamp);
|
||||
|
||||
|
||||
|
||||
#ifdef PICO_SUPPORT_MM_PROFILING
|
||||
/***********************************************************************************************************************
|
||||
***********************************************************************************************************************
|
||||
MEMORY PROFILING FUNCTIONS
|
||||
***********************************************************************************************************************
|
||||
***********************************************************************************************************************/
|
||||
/* General info struct */
|
||||
struct profiling_data
|
||||
{
|
||||
uint32_t free_heap_space;
|
||||
uint32_t free_slab_space;
|
||||
uint32_t used_heap_space;
|
||||
uint32_t used_slab_space;
|
||||
};
|
||||
|
||||
/*
|
||||
* This function fills up a struct with used and free slab and heap space in the memory manager
|
||||
* The user is responsible for resource managment
|
||||
*/
|
||||
void pico_mem_profile_collect_data(struct profiling_data*profiling_page_struct);
|
||||
|
||||
/*
|
||||
* This function prints the general structure of the memory manager
|
||||
* Printf in this function can be rerouted to send this data over a serial port, or to write it away to memory
|
||||
*/
|
||||
void pico_mem_profile_scan_data(void);
|
||||
|
||||
/*
|
||||
* This function returns the total size that the manager has received from the system
|
||||
* This can give an indication of the total system resource commitment, but keep in mind that
|
||||
* there can be many free blocks in this "used" size
|
||||
* Together with pico_mem_profile_collect_data, this can give a good estimation of the total
|
||||
* resource commitment
|
||||
*/
|
||||
uint32_t pico_mem_profile_used_size(void);
|
||||
|
||||
/*
|
||||
* This function returns a pointer to page 0, the main memory manager housekeeping (struct pico_mem_manager).
|
||||
* This can be used to collect data about the memory in user defined functions.
|
||||
* Use with care!
|
||||
*/
|
||||
void*pico_mem_profile_manager(void);
|
||||
|
||||
/*
|
||||
* paramter manager is a pointer to a struct pico_mem_manager
|
||||
*/
|
||||
void pico_mem_init_profiling(void*manager, uint32_t memsize);
|
||||
#endif /* PICO_SUPPORT_MM_PROFILING */
|
||||
|
||||
#endif /* _INCLUDE_PICO_MM */
|
||||
589
kernel/picotcp/modules/pico_nat.c
Normal file
589
kernel/picotcp/modules/pico_nat.c
Normal file
@ -0,0 +1,589 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Kristof Roelants, Brecht Van Cauwenberghe,
|
||||
Simon Maes, Philippe Mariman
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_stack.h"
|
||||
#include "pico_frame.h"
|
||||
#include "pico_tcp.h"
|
||||
#include "pico_udp.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_nat.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
#ifdef PICO_SUPPORT_NAT
|
||||
|
||||
#ifdef DEBUG_NAT
|
||||
#define nat_dbg dbg
|
||||
#else
|
||||
#define nat_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define PICO_NAT_TIMEWAIT 240000 /* msec (4 mins) */
|
||||
|
||||
#define PICO_NAT_INBOUND 0
|
||||
#define PICO_NAT_OUTBOUND 1
|
||||
|
||||
struct pico_nat_tuple {
|
||||
uint8_t proto;
|
||||
uint16_t conn_active : 11;
|
||||
uint16_t portforward : 1;
|
||||
uint16_t rst : 1;
|
||||
uint16_t syn : 1;
|
||||
uint16_t fin_in : 1;
|
||||
uint16_t fin_out : 1;
|
||||
uint16_t src_port;
|
||||
uint16_t dst_port;
|
||||
uint16_t nat_port;
|
||||
struct pico_ip4 src_addr;
|
||||
struct pico_ip4 dst_addr;
|
||||
struct pico_ip4 nat_addr;
|
||||
};
|
||||
|
||||
static struct pico_ipv4_link *nat_link = NULL;
|
||||
|
||||
static int nat_cmp_natport(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
|
||||
{
|
||||
|
||||
if (a->nat_port < b->nat_port)
|
||||
return -1;
|
||||
|
||||
if (a->nat_port > b->nat_port)
|
||||
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int nat_cmp_srcport(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
|
||||
{
|
||||
|
||||
if (a->src_port < b->src_port)
|
||||
return -1;
|
||||
|
||||
if (a->src_port > b->src_port)
|
||||
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int nat_cmp_proto(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
|
||||
{
|
||||
if (a->proto < b->proto)
|
||||
return -1;
|
||||
|
||||
if (a->proto > b->proto)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nat_cmp_address(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
|
||||
{
|
||||
return pico_ipv4_compare(&a->src_addr, &b->src_addr);
|
||||
}
|
||||
|
||||
static int nat_cmp_inbound(void *ka, void *kb)
|
||||
{
|
||||
struct pico_nat_tuple *a = ka, *b = kb;
|
||||
int cport = nat_cmp_natport(a, b);
|
||||
if (cport)
|
||||
return cport;
|
||||
|
||||
return nat_cmp_proto(a, b);
|
||||
}
|
||||
|
||||
|
||||
static int nat_cmp_outbound(void *ka, void *kb)
|
||||
{
|
||||
struct pico_nat_tuple *a = ka, *b = kb;
|
||||
int caddr, cport;
|
||||
|
||||
caddr = nat_cmp_address(a, b);
|
||||
if (caddr)
|
||||
return caddr;
|
||||
|
||||
cport = nat_cmp_srcport(a, b);
|
||||
|
||||
if (cport)
|
||||
return cport;
|
||||
|
||||
return nat_cmp_proto(a, b);
|
||||
}
|
||||
|
||||
static PICO_TREE_DECLARE(NATOutbound, nat_cmp_outbound);
|
||||
static PICO_TREE_DECLARE(NATInbound, nat_cmp_inbound);
|
||||
|
||||
void pico_ipv4_nat_print_table(void)
|
||||
{
|
||||
struct pico_nat_tuple *t = NULL;
|
||||
struct pico_tree_node *index = NULL;
|
||||
(void)t;
|
||||
|
||||
nat_dbg("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||||
nat_dbg("+ NAT table +\n");
|
||||
nat_dbg("+------------------------------------------------------------------------------------------------------------------------+\n");
|
||||
nat_dbg("+ src_addr | src_port | dst_addr | dst_port | nat_addr | nat_port | proto | conn active | FIN1 | FIN2 | SYN | RST | FORW +\n");
|
||||
nat_dbg("+------------------------------------------------------------------------------------------------------------------------+\n");
|
||||
|
||||
pico_tree_foreach(index, &NATOutbound)
|
||||
{
|
||||
t = index->keyValue;
|
||||
nat_dbg("+ %08X | %05u | %08X | %05u | %08X | %05u | %03u | %03u | %u | %u | %u | %u | %u +\n",
|
||||
long_be(t->src_addr.addr), t->src_port, long_be(t->dst_addr.addr), t->dst_port, long_be(t->nat_addr.addr), t->nat_port,
|
||||
t->proto, t->conn_active, t->fin_in, t->fin_out, t->syn, t->rst, t->portforward);
|
||||
}
|
||||
nat_dbg("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||||
}
|
||||
|
||||
/*
|
||||
2 options:
|
||||
find on nat_port and proto
|
||||
find on src_addr, src_port and proto
|
||||
zero the unused parameters
|
||||
*/
|
||||
static struct pico_nat_tuple *pico_ipv4_nat_find_tuple(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto)
|
||||
{
|
||||
struct pico_nat_tuple *found = NULL, test = {
|
||||
0
|
||||
};
|
||||
|
||||
test.nat_port = nat_port;
|
||||
test.src_port = src_port;
|
||||
test.proto = proto;
|
||||
if (src_addr)
|
||||
test.src_addr = *src_addr;
|
||||
|
||||
if (nat_port)
|
||||
found = pico_tree_findKey(&NATInbound, &test);
|
||||
else
|
||||
found = pico_tree_findKey(&NATOutbound, &test);
|
||||
|
||||
if (found)
|
||||
return found;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int pico_ipv4_nat_find(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto)
|
||||
{
|
||||
struct pico_nat_tuple *t = NULL;
|
||||
|
||||
t = pico_ipv4_nat_find_tuple(nat_port, src_addr, src_port, proto);
|
||||
if (t)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pico_nat_tuple *pico_ipv4_nat_add(struct pico_ip4 dst_addr, uint16_t dst_port, struct pico_ip4 src_addr, uint16_t src_port,
|
||||
struct pico_ip4 nat_addr, uint16_t nat_port, uint8_t proto)
|
||||
{
|
||||
struct pico_nat_tuple *t = PICO_ZALLOC(sizeof(struct pico_nat_tuple));
|
||||
if (!t) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t->dst_addr = dst_addr;
|
||||
t->dst_port = dst_port;
|
||||
t->src_addr = src_addr;
|
||||
t->src_port = src_port;
|
||||
t->nat_addr = nat_addr;
|
||||
t->nat_port = nat_port;
|
||||
t->proto = proto;
|
||||
t->conn_active = 1;
|
||||
t->portforward = 0;
|
||||
t->rst = 0;
|
||||
t->syn = 0;
|
||||
t->fin_in = 0;
|
||||
t->fin_out = 0;
|
||||
|
||||
if (pico_tree_insert(&NATOutbound, t)) {
|
||||
PICO_FREE(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pico_tree_insert(&NATInbound, t)) {
|
||||
pico_tree_delete(&NATOutbound, t);
|
||||
PICO_FREE(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int pico_ipv4_nat_del(uint16_t nat_port, uint8_t proto)
|
||||
{
|
||||
struct pico_nat_tuple *t = NULL;
|
||||
t = pico_ipv4_nat_find_tuple(nat_port, NULL, 0, proto);
|
||||
if (t) {
|
||||
pico_tree_delete(&NATOutbound, t);
|
||||
pico_tree_delete(&NATInbound, t);
|
||||
PICO_FREE(t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pico_trans *pico_nat_generate_tuple_trans(struct pico_ipv4_hdr *net, struct pico_frame *f)
|
||||
{
|
||||
struct pico_trans *trans = NULL;
|
||||
switch (net->proto) {
|
||||
case PICO_PROTO_TCP:
|
||||
{
|
||||
struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
|
||||
trans = (struct pico_trans *)&tcp->trans;
|
||||
break;
|
||||
}
|
||||
case PICO_PROTO_UDP:
|
||||
{
|
||||
struct pico_udp_hdr *udp = (struct pico_udp_hdr *)f->transport_hdr;
|
||||
trans = (struct pico_trans *)&udp->trans;
|
||||
break;
|
||||
}
|
||||
case PICO_PROTO_ICMP4:
|
||||
/* XXX: implement */
|
||||
break;
|
||||
}
|
||||
return trans;
|
||||
}
|
||||
|
||||
static struct pico_nat_tuple *pico_ipv4_nat_generate_tuple(struct pico_frame *f)
|
||||
{
|
||||
struct pico_trans *trans = NULL;
|
||||
struct pico_ipv4_hdr *net = (struct pico_ipv4_hdr *)f->net_hdr;
|
||||
uint16_t nport = 0;
|
||||
uint8_t retry = 32;
|
||||
|
||||
/* generate NAT port */
|
||||
do {
|
||||
uint32_t rand = pico_rand();
|
||||
nport = (uint16_t) (rand & 0xFFFFU);
|
||||
nport = (uint16_t)((nport % (65535 - 1024)) + 1024U);
|
||||
nport = short_be(nport);
|
||||
|
||||
if (pico_is_port_free(net->proto, nport, NULL, &pico_proto_ipv4))
|
||||
break;
|
||||
} while (--retry);
|
||||
|
||||
if (!retry)
|
||||
return NULL;
|
||||
|
||||
trans = pico_nat_generate_tuple_trans(net, f);
|
||||
if(!trans)
|
||||
return NULL;
|
||||
|
||||
return pico_ipv4_nat_add(net->dst, trans->dport, net->src, trans->sport, nat_link->address, nport, net->proto);
|
||||
/* XXX return pico_ipv4_nat_add(nat_link->address, port, net->src, trans->sport, net->proto); */
|
||||
}
|
||||
|
||||
static inline void pico_ipv4_nat_set_tcp_flags(struct pico_nat_tuple *t, struct pico_frame *f, uint8_t direction)
|
||||
{
|
||||
struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
|
||||
if (tcp->flags & PICO_TCP_SYN)
|
||||
t->syn = 1;
|
||||
|
||||
if (tcp->flags & PICO_TCP_RST)
|
||||
t->rst = 1;
|
||||
|
||||
if ((tcp->flags & PICO_TCP_FIN) && (direction == PICO_NAT_INBOUND))
|
||||
t->fin_in = 1;
|
||||
|
||||
if ((tcp->flags & PICO_TCP_FIN) && (direction == PICO_NAT_OUTBOUND))
|
||||
t->fin_out = 1;
|
||||
}
|
||||
|
||||
static int pico_ipv4_nat_sniff_session(struct pico_nat_tuple *t, struct pico_frame *f, uint8_t direction)
|
||||
{
|
||||
struct pico_ipv4_hdr *net = (struct pico_ipv4_hdr *)f->net_hdr;
|
||||
|
||||
switch (net->proto) {
|
||||
case PICO_PROTO_TCP:
|
||||
{
|
||||
pico_ipv4_nat_set_tcp_flags(t, f, direction);
|
||||
break;
|
||||
}
|
||||
|
||||
case PICO_PROTO_UDP:
|
||||
t->conn_active = 1;
|
||||
break;
|
||||
|
||||
case PICO_PROTO_ICMP4:
|
||||
/* XXX: implement */
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pico_ipv4_nat_table_cleanup(pico_time now, void *_unused)
|
||||
{
|
||||
struct pico_tree_node *index = NULL, *_tmp = NULL;
|
||||
struct pico_nat_tuple *t = NULL;
|
||||
IGNORE_PARAMETER(now);
|
||||
IGNORE_PARAMETER(_unused);
|
||||
nat_dbg("NAT: before table cleanup:\n");
|
||||
pico_ipv4_nat_print_table();
|
||||
|
||||
pico_tree_foreach_reverse_safe(index, &NATOutbound, _tmp)
|
||||
{
|
||||
t = index->keyValue;
|
||||
switch (t->proto)
|
||||
{
|
||||
case PICO_PROTO_TCP:
|
||||
if (t->portforward)
|
||||
break;
|
||||
else if (t->conn_active == 0 || t->conn_active > 360) /* conn active for > 24 hours */
|
||||
pico_ipv4_nat_del(t->nat_port, t->proto);
|
||||
else if (t->rst || (t->fin_in && t->fin_out))
|
||||
t->conn_active = 0;
|
||||
else
|
||||
t->conn_active++;
|
||||
|
||||
break;
|
||||
|
||||
case PICO_PROTO_UDP:
|
||||
if (t->portforward)
|
||||
break;
|
||||
else if (t->conn_active > 1)
|
||||
pico_ipv4_nat_del(t->nat_port, t->proto);
|
||||
else
|
||||
t->conn_active++;
|
||||
|
||||
break;
|
||||
|
||||
case PICO_PROTO_ICMP4:
|
||||
if (t->conn_active > 1)
|
||||
pico_ipv4_nat_del(t->nat_port, t->proto);
|
||||
else
|
||||
t->conn_active++;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown protocol in NAT table, delete when it has existed NAT_TIMEWAIT */
|
||||
if (t->conn_active > 1)
|
||||
pico_ipv4_nat_del(t->nat_port, t->proto);
|
||||
else
|
||||
t->conn_active++;
|
||||
}
|
||||
}
|
||||
|
||||
nat_dbg("NAT: after table cleanup:\n");
|
||||
pico_ipv4_nat_print_table();
|
||||
if (!pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL)) {
|
||||
nat_dbg("NAT: Failed to start cleanup timer\n");
|
||||
/* TODO no more NAT table cleanup now */
|
||||
}
|
||||
}
|
||||
|
||||
int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag)
|
||||
{
|
||||
struct pico_nat_tuple *t = NULL;
|
||||
struct pico_ip4 any_addr = {
|
||||
0
|
||||
};
|
||||
uint16_t any_port = 0;
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case PICO_NAT_PORT_FORWARD_ADD:
|
||||
t = pico_ipv4_nat_add(any_addr, any_port, src_addr, src_port, nat_addr, nat_port, proto);
|
||||
if (!t) {
|
||||
pico_err = PICO_ERR_EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
t->portforward = 1;
|
||||
break;
|
||||
|
||||
case PICO_NAT_PORT_FORWARD_DEL:
|
||||
return pico_ipv4_nat_del(nat_port, proto);
|
||||
|
||||
default:
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_ipv4_nat_print_table();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_ipv4_nat_inbound(struct pico_frame *f, struct pico_ip4 *link_addr)
|
||||
{
|
||||
struct pico_nat_tuple *tuple = NULL;
|
||||
struct pico_trans *trans = NULL;
|
||||
struct pico_ipv4_hdr *net = (struct pico_ipv4_hdr *)f->net_hdr;
|
||||
|
||||
if (!pico_ipv4_nat_is_enabled(link_addr))
|
||||
return -1;
|
||||
|
||||
switch (net->proto) {
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
case PICO_PROTO_TCP:
|
||||
{
|
||||
struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
|
||||
trans = (struct pico_trans *)&tcp->trans;
|
||||
tuple = pico_ipv4_nat_find_tuple(trans->dport, 0, 0, net->proto);
|
||||
if (!tuple)
|
||||
return -1;
|
||||
|
||||
/* replace dst IP and dst PORT */
|
||||
net->dst = tuple->src_addr;
|
||||
trans->dport = tuple->src_port;
|
||||
/* recalculate CRC */
|
||||
tcp->crc = 0;
|
||||
tcp->crc = short_be(pico_tcp_checksum_ipv4(f));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
case PICO_PROTO_UDP:
|
||||
{
|
||||
struct pico_udp_hdr *udp = (struct pico_udp_hdr *)f->transport_hdr;
|
||||
trans = (struct pico_trans *)&udp->trans;
|
||||
tuple = pico_ipv4_nat_find_tuple(trans->dport, 0, 0, net->proto);
|
||||
if (!tuple)
|
||||
return -1;
|
||||
|
||||
/* replace dst IP and dst PORT */
|
||||
net->dst = tuple->src_addr;
|
||||
trans->dport = tuple->src_port;
|
||||
/* recalculate CRC */
|
||||
udp->crc = 0;
|
||||
udp->crc = short_be(pico_udp_checksum_ipv4(f));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case PICO_PROTO_ICMP4:
|
||||
/* XXX reimplement */
|
||||
break;
|
||||
|
||||
default:
|
||||
nat_dbg("NAT ERROR: inbound NAT on erroneous protocol\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_ipv4_nat_sniff_session(tuple, f, PICO_NAT_INBOUND);
|
||||
net->crc = 0;
|
||||
net->crc = short_be(pico_checksum(net, f->net_len));
|
||||
|
||||
nat_dbg("NAT: inbound translation {dst.addr, dport}: {%08X,%u} -> {%08X,%u}\n",
|
||||
tuple->nat_addr.addr, short_be(tuple->nat_port), tuple->src_addr.addr, short_be(tuple->src_port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_ipv4_nat_outbound(struct pico_frame *f, struct pico_ip4 *link_addr)
|
||||
{
|
||||
struct pico_nat_tuple *tuple = NULL;
|
||||
struct pico_trans *trans = NULL;
|
||||
struct pico_ipv4_hdr *net = (struct pico_ipv4_hdr *)f->net_hdr;
|
||||
|
||||
if (!pico_ipv4_nat_is_enabled(link_addr))
|
||||
return -1;
|
||||
|
||||
switch (net->proto) {
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
case PICO_PROTO_TCP:
|
||||
{
|
||||
struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
|
||||
trans = (struct pico_trans *)&tcp->trans;
|
||||
tuple = pico_ipv4_nat_find_tuple(0, &net->src, trans->sport, net->proto);
|
||||
if (!tuple)
|
||||
tuple = pico_ipv4_nat_generate_tuple(f);
|
||||
|
||||
/* replace src IP and src PORT */
|
||||
net->src = tuple->nat_addr;
|
||||
trans->sport = tuple->nat_port;
|
||||
/* recalculate CRC */
|
||||
tcp->crc = 0;
|
||||
tcp->crc = short_be(pico_tcp_checksum_ipv4(f));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
case PICO_PROTO_UDP:
|
||||
{
|
||||
struct pico_udp_hdr *udp = (struct pico_udp_hdr *)f->transport_hdr;
|
||||
trans = (struct pico_trans *)&udp->trans;
|
||||
tuple = pico_ipv4_nat_find_tuple(0, &net->src, trans->sport, net->proto);
|
||||
if (!tuple)
|
||||
tuple = pico_ipv4_nat_generate_tuple(f);
|
||||
|
||||
/* replace src IP and src PORT */
|
||||
net->src = tuple->nat_addr;
|
||||
trans->sport = tuple->nat_port;
|
||||
/* recalculate CRC */
|
||||
udp->crc = 0;
|
||||
udp->crc = short_be(pico_udp_checksum_ipv4(f));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case PICO_PROTO_ICMP4:
|
||||
/* XXX reimplement */
|
||||
break;
|
||||
|
||||
default:
|
||||
nat_dbg("NAT ERROR: outbound NAT on erroneous protocol\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_ipv4_nat_sniff_session(tuple, f, PICO_NAT_OUTBOUND);
|
||||
net->crc = 0;
|
||||
net->crc = short_be(pico_checksum(net, f->net_len));
|
||||
|
||||
nat_dbg("NAT: outbound translation {src.addr, sport}: {%08X,%u} -> {%08X,%u}\n",
|
||||
tuple->src_addr.addr, short_be(tuple->src_port), tuple->nat_addr.addr, short_be(tuple->nat_port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_ipv4_nat_enable(struct pico_ipv4_link *link)
|
||||
{
|
||||
if (link == NULL) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL)) {
|
||||
nat_dbg("NAT: Failed to start cleanup timer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
nat_link = link;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_ipv4_nat_disable(void)
|
||||
{
|
||||
nat_link = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_ipv4_nat_is_enabled(struct pico_ip4 *link_addr)
|
||||
{
|
||||
if (!nat_link)
|
||||
return 0;
|
||||
|
||||
if (nat_link->address.addr != link_addr->addr)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
90
kernel/picotcp/modules/pico_nat.h
Normal file
90
kernel/picotcp/modules/pico_nat.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef INCLUDE_PICO_NAT
|
||||
#define INCLUDE_PICO_NAT
|
||||
#include "pico_frame.h"
|
||||
|
||||
#define PICO_NAT_PORT_FORWARD_DEL 0
|
||||
#define PICO_NAT_PORT_FORWARD_ADD 1
|
||||
|
||||
#ifdef PICO_SUPPORT_NAT
|
||||
void pico_ipv4_nat_print_table(void);
|
||||
int pico_ipv4_nat_find(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto);
|
||||
int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag);
|
||||
|
||||
int pico_ipv4_nat_inbound(struct pico_frame *f, struct pico_ip4 *link_addr);
|
||||
int pico_ipv4_nat_outbound(struct pico_frame *f, struct pico_ip4 *link_addr);
|
||||
int pico_ipv4_nat_enable(struct pico_ipv4_link *link);
|
||||
int pico_ipv4_nat_disable(void);
|
||||
int pico_ipv4_nat_is_enabled(struct pico_ip4 *link_addr);
|
||||
#else
|
||||
|
||||
#define pico_ipv4_nat_print_table() do {} while(0)
|
||||
static inline int pico_ipv4_nat_inbound(struct pico_frame *f, struct pico_ip4 *link_addr)
|
||||
{
|
||||
(void)f;
|
||||
(void)link_addr;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int pico_ipv4_nat_outbound(struct pico_frame *f, struct pico_ip4 *link_addr)
|
||||
{
|
||||
(void)f;
|
||||
(void)link_addr;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int pico_ipv4_nat_enable(struct pico_ipv4_link *link)
|
||||
{
|
||||
(void)link;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int pico_ipv4_nat_disable(void)
|
||||
{
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int pico_ipv4_nat_is_enabled(struct pico_ip4 *link_addr)
|
||||
{
|
||||
(void)link_addr;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int pico_ipv4_nat_find(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto)
|
||||
{
|
||||
(void)nat_port;
|
||||
(void)src_addr;
|
||||
(void)src_port;
|
||||
(void)proto;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag)
|
||||
{
|
||||
(void)nat_addr;
|
||||
(void)nat_port;
|
||||
(void)src_addr;
|
||||
(void)src_port;
|
||||
(void)proto;
|
||||
(void)flag;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INCLUDE_PICO_NAT */
|
||||
|
||||
1167
kernel/picotcp/modules/pico_olsr.c
Normal file
1167
kernel/picotcp/modules/pico_olsr.c
Normal file
File diff suppressed because it is too large
Load Diff
32
kernel/picotcp/modules/pico_olsr.h
Normal file
32
kernel/picotcp/modules/pico_olsr.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
#ifndef PICO_OLSR_H
|
||||
#define PICO_OLSR_H
|
||||
|
||||
|
||||
/* Objects */
|
||||
struct olsr_route_entry
|
||||
{
|
||||
struct olsr_route_entry *next;
|
||||
uint32_t time_left;
|
||||
struct pico_ip4 destination;
|
||||
struct olsr_route_entry *gateway;
|
||||
struct pico_device *iface;
|
||||
uint16_t metric;
|
||||
uint8_t link_type;
|
||||
struct olsr_route_entry *children;
|
||||
uint16_t ansn;
|
||||
uint16_t seq;
|
||||
uint8_t lq, nlq;
|
||||
uint8_t *advertised_tc;
|
||||
};
|
||||
|
||||
|
||||
void pico_olsr_init(void);
|
||||
int pico_olsr_add(struct pico_device *dev);
|
||||
struct olsr_route_entry *olsr_get_ethentry(struct pico_device *vif);
|
||||
#endif
|
||||
99
kernel/picotcp/modules/pico_posix.c
Normal file
99
kernel/picotcp/modules/pico_posix.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Andrei Carp, Maarten Vandersteegen
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef PICO_SUPPORT_THREADING
|
||||
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include "pico_config.h"
|
||||
|
||||
/* POSIX mutex implementation */
|
||||
void *pico_mutex_init(void)
|
||||
{
|
||||
pthread_mutex_t *m;
|
||||
m = (pthread_mutex_t *)PICO_ZALLOC(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(m, NULL);
|
||||
return m;
|
||||
}
|
||||
|
||||
void pico_mutex_destroy(void *mux)
|
||||
{
|
||||
PICO_FREE(mux);
|
||||
mux = NULL;
|
||||
}
|
||||
|
||||
void pico_mutex_lock(void *mux)
|
||||
{
|
||||
if (mux == NULL) return;
|
||||
|
||||
pthread_mutex_t *m = (pthread_mutex_t *)mux;
|
||||
pthread_mutex_lock(m);
|
||||
}
|
||||
|
||||
void pico_mutex_unlock(void *mux)
|
||||
{
|
||||
if (mux == NULL) return;
|
||||
|
||||
pthread_mutex_t *m = (pthread_mutex_t *)mux;
|
||||
pthread_mutex_unlock(m);
|
||||
}
|
||||
|
||||
/* POSIX semaphore implementation */
|
||||
void *pico_sem_init(void)
|
||||
{
|
||||
sem_t *s;
|
||||
s = (sem_t *)PICO_ZALLOC(sizeof(sem_t));
|
||||
sem_init(s, 0, 0);
|
||||
return s;
|
||||
}
|
||||
|
||||
void pico_sem_destroy(void *sem)
|
||||
{
|
||||
PICO_FREE(sem);
|
||||
sem = NULL;
|
||||
}
|
||||
|
||||
void pico_sem_post(void *sem)
|
||||
{
|
||||
if (sem == NULL) return;
|
||||
|
||||
sem_t *s = (sem_t *)sem;
|
||||
sem_post(s);
|
||||
}
|
||||
|
||||
int pico_sem_wait(void *sem, int timeout)
|
||||
{
|
||||
struct timespec t;
|
||||
if (sem == NULL) return 0;
|
||||
|
||||
sem_t *s = (sem_t *)sem;
|
||||
|
||||
if (timeout < 0) {
|
||||
sem_wait(s);
|
||||
} else {
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
t.tv_sec += timeout / 1000;
|
||||
t.tv_nsec += (timeout % 1000) * 1000000;
|
||||
if (sem_timedwait(s, &t) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* POSIX thread implementation */
|
||||
void *pico_thread_create(void *(*routine)(void *), void *arg)
|
||||
{
|
||||
pthread_t *thread;
|
||||
thread = (pthread_t *)PICO_ZALLOC(sizeof(pthread_t));
|
||||
|
||||
if (pthread_create(thread, NULL, routine, arg) == -1)
|
||||
return NULL;
|
||||
|
||||
return thread;
|
||||
}
|
||||
#endif /* PICO_SUPPORT_THREADING */
|
||||
307
kernel/picotcp/modules/pico_slaacv4.c
Normal file
307
kernel/picotcp/modules/pico_slaacv4.c
Normal file
@ -0,0 +1,307 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Bogdan Lupu
|
||||
*********************************************************************/
|
||||
#include "pico_slaacv4.h"
|
||||
#include "pico_arp.h"
|
||||
#include "pico_constants.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_hotplug_detection.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_SLAACV4
|
||||
|
||||
#ifdef DEBUG_SLAACV4
|
||||
#define slaacv4_dbg dbg
|
||||
#else
|
||||
#define slaacv4_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define SLAACV4_NETWORK ((long_be(0xa9fe0000)))
|
||||
#define SLAACV4_NETMASK ((long_be(0xFFFF0000)))
|
||||
#define SLAACV4_MINRANGE (0x00000100) /* In host order */
|
||||
#define SLAACV4_MAXRANGE (0x0000FDFF) /* In host order */
|
||||
|
||||
#define SLAACV4_CREATE_IPV4(seed) ((long_be((seed % SLAACV4_MAXRANGE) + SLAACV4_MINRANGE) & ~SLAACV4_NETMASK) | SLAACV4_NETWORK)
|
||||
|
||||
#define PROBE_WAIT 1 /* delay between two tries during claim */
|
||||
#define PROBE_NB 3 /* number of probe packets during claim */
|
||||
/* #define PROBE_MIN 1 */
|
||||
/* #define PROBE_MAX 2 */
|
||||
#define ANNOUNCE_WAIT 2 /* delay before start announcing */
|
||||
#define ANNOUNCE_NB 2 /* number of announcement packets */
|
||||
#define ANNOUNCE_INTERVAL 2 /* time between announcement packets */
|
||||
#define MAX_CONFLICTS 10 /* max conflicts before rate limiting */
|
||||
#define MAX_CONFLICTS_FAIL 20 /* max conflicts before declaring failure */
|
||||
#define RATE_LIMIT_INTERVAL 60 /* time between successive attempts */
|
||||
#define DEFEND_INTERVAL 10 /* minimum interval between defensive ARP */
|
||||
|
||||
enum slaacv4_state {
|
||||
SLAACV4_RESET = 0,
|
||||
SLAACV4_CLAIMING,
|
||||
SLAACV4_CLAIMED,
|
||||
SLAACV4_ANNOUNCING,
|
||||
SLAACV4_ERROR
|
||||
};
|
||||
|
||||
struct slaacv4_cookie {
|
||||
enum slaacv4_state state;
|
||||
uint8_t probe_try_nb;
|
||||
uint8_t conflict_nb;
|
||||
uint8_t announce_nb;
|
||||
struct pico_ip4 ip;
|
||||
struct pico_device *device;
|
||||
uint32_t timer;
|
||||
void (*cb)(struct pico_ip4 *ip, uint8_t code);
|
||||
};
|
||||
|
||||
static void pico_slaacv4_hotplug_cb(struct pico_device *dev, int event);
|
||||
|
||||
static struct slaacv4_cookie slaacv4_local;
|
||||
|
||||
static uint32_t pico_slaacv4_getip(struct pico_device *dev, uint8_t rand)
|
||||
{
|
||||
uint32_t seed = 0;
|
||||
if (dev->eth != NULL)
|
||||
{
|
||||
seed = pico_hash((const uint8_t *)dev->eth->mac.addr, PICO_SIZE_ETH);
|
||||
}
|
||||
|
||||
if (rand)
|
||||
{
|
||||
seed += pico_rand();
|
||||
}
|
||||
|
||||
return SLAACV4_CREATE_IPV4(seed);
|
||||
}
|
||||
|
||||
static void pico_slaacv4_init_cookie(struct pico_ip4 *ip, struct pico_device *dev, struct slaacv4_cookie *ck, void (*cb)(struct pico_ip4 *ip, uint8_t code))
|
||||
{
|
||||
ck->state = SLAACV4_RESET;
|
||||
ck->probe_try_nb = 0;
|
||||
ck->conflict_nb = 0;
|
||||
ck->announce_nb = 0;
|
||||
ck->cb = cb;
|
||||
ck->device = dev;
|
||||
ck->ip.addr = ip->addr;
|
||||
ck->timer = 0;
|
||||
}
|
||||
|
||||
static void pico_slaacv4_cancel_timers(struct slaacv4_cookie *tmp)
|
||||
{
|
||||
pico_timer_cancel(tmp->timer);
|
||||
tmp->timer = 0;
|
||||
}
|
||||
|
||||
static void pico_slaacv4_send_announce_timer(pico_time now, void *arg)
|
||||
{
|
||||
struct slaacv4_cookie *tmp = (struct slaacv4_cookie *)arg;
|
||||
struct pico_ip4 netmask = {
|
||||
0
|
||||
};
|
||||
netmask.addr = long_be(0xFFFF0000);
|
||||
|
||||
(void)now;
|
||||
|
||||
if (tmp->announce_nb < ANNOUNCE_NB)
|
||||
{
|
||||
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_ANNOUNCE);
|
||||
tmp->announce_nb++;
|
||||
tmp->timer = pico_timer_add(ANNOUNCE_INTERVAL * 1000, pico_slaacv4_send_announce_timer, arg);
|
||||
if (!tmp->timer) {
|
||||
slaacv4_dbg("SLAACV4: Failed to start announce timer\n");
|
||||
tmp->state = SLAACV4_ERROR;
|
||||
if (tmp->cb != NULL)
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp->state = SLAACV4_CLAIMED;
|
||||
pico_ipv4_link_add(tmp->device, tmp->ip, netmask);
|
||||
if (tmp->cb != NULL)
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
static void pico_slaacv4_send_probe_timer(pico_time now, void *arg)
|
||||
{
|
||||
struct slaacv4_cookie *tmp = (struct slaacv4_cookie *)arg;
|
||||
(void)now;
|
||||
|
||||
if (tmp->probe_try_nb < PROBE_NB)
|
||||
{
|
||||
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_PROBE);
|
||||
tmp->probe_try_nb++;
|
||||
tmp->timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, tmp);
|
||||
if (!tmp->timer) {
|
||||
slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
|
||||
tmp->state = SLAACV4_ERROR;
|
||||
if (tmp->cb != NULL)
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp->state = SLAACV4_ANNOUNCING;
|
||||
tmp->timer = pico_timer_add(ANNOUNCE_WAIT * 1000, pico_slaacv4_send_announce_timer, arg);
|
||||
if (!tmp->timer) {
|
||||
slaacv4_dbg("SLAACV4: Failed to start announce timer\n");
|
||||
tmp->state = SLAACV4_ERROR;
|
||||
if (tmp->cb != NULL)
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pico_slaacv4_receive_ipconflict(int reason)
|
||||
{
|
||||
struct slaacv4_cookie *tmp = &slaacv4_local;
|
||||
|
||||
tmp->conflict_nb++;
|
||||
pico_slaacv4_cancel_timers(tmp);
|
||||
|
||||
if(tmp->state == SLAACV4_CLAIMED)
|
||||
{
|
||||
if(reason == PICO_ARP_CONFLICT_REASON_CONFLICT)
|
||||
{
|
||||
pico_ipv4_link_del(tmp->device, tmp->ip);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp->conflict_nb < MAX_CONFLICTS)
|
||||
{
|
||||
tmp->state = SLAACV4_CLAIMING;
|
||||
tmp->probe_try_nb = 0;
|
||||
tmp->announce_nb = 0;
|
||||
tmp->ip.addr = pico_slaacv4_getip(tmp->device, (uint8_t)1);
|
||||
pico_arp_register_ipconflict(&tmp->ip, &tmp->device->eth->mac, pico_slaacv4_receive_ipconflict);
|
||||
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_PROBE);
|
||||
tmp->probe_try_nb++;
|
||||
tmp->timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, tmp);
|
||||
if (!tmp->timer) {
|
||||
slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
|
||||
tmp->state = SLAACV4_ERROR;
|
||||
if (tmp->cb != NULL)
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
|
||||
}
|
||||
}
|
||||
else if (tmp->conflict_nb < MAX_CONFLICTS_FAIL)
|
||||
{
|
||||
tmp->state = SLAACV4_CLAIMING;
|
||||
tmp->probe_try_nb = 0;
|
||||
tmp->announce_nb = 0;
|
||||
tmp->ip.addr = pico_slaacv4_getip(tmp->device, (uint8_t)1);
|
||||
pico_arp_register_ipconflict(&tmp->ip, &tmp->device->eth->mac, pico_slaacv4_receive_ipconflict);
|
||||
tmp->timer = pico_timer_add(RATE_LIMIT_INTERVAL * 1000, pico_slaacv4_send_probe_timer, tmp);
|
||||
if (!tmp->timer) {
|
||||
slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
|
||||
tmp->state = SLAACV4_ERROR;
|
||||
if (tmp->cb != NULL)
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tmp->cb != NULL)
|
||||
{
|
||||
pico_hotplug_deregister(tmp->device, &pico_slaacv4_hotplug_cb);
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
|
||||
}
|
||||
|
||||
tmp->state = SLAACV4_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void pico_slaacv4_hotplug_cb(__attribute__((unused)) struct pico_device *dev, int event)
|
||||
{
|
||||
struct slaacv4_cookie *tmp = &slaacv4_local;
|
||||
|
||||
if (event == PICO_HOTPLUG_EVENT_UP )
|
||||
{
|
||||
slaacv4_local.state = SLAACV4_CLAIMING;
|
||||
tmp->probe_try_nb = 0;
|
||||
tmp->announce_nb = 0;
|
||||
|
||||
pico_arp_register_ipconflict(&tmp->ip, &tmp->device->eth->mac, pico_slaacv4_receive_ipconflict);
|
||||
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_PROBE);
|
||||
tmp->probe_try_nb++;
|
||||
tmp->timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, tmp);
|
||||
if (!tmp->timer) {
|
||||
slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
|
||||
tmp->state = SLAACV4_ERROR;
|
||||
if (tmp->cb != NULL)
|
||||
tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tmp->state == SLAACV4_CLAIMED )
|
||||
pico_ipv4_link_del(tmp->device, tmp->ip);
|
||||
|
||||
pico_slaacv4_cancel_timers(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
int pico_slaacv4_claimip(struct pico_device *dev, void (*cb)(struct pico_ip4 *ip, uint8_t code))
|
||||
{
|
||||
struct pico_ip4 ip;
|
||||
|
||||
if (!dev->eth) {
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( dev->link_state != NULL )
|
||||
{
|
||||
/* hotplug detect will work */
|
||||
|
||||
ip.addr = pico_slaacv4_getip(dev, 0);
|
||||
pico_slaacv4_init_cookie(&ip, dev, &slaacv4_local, cb);
|
||||
|
||||
if (pico_hotplug_register(dev, &pico_slaacv4_hotplug_cb))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ip.addr = pico_slaacv4_getip(dev, 0);
|
||||
|
||||
pico_slaacv4_init_cookie(&ip, dev, &slaacv4_local, cb);
|
||||
pico_arp_register_ipconflict(&ip, &dev->eth->mac, pico_slaacv4_receive_ipconflict);
|
||||
pico_arp_request(dev, &ip, PICO_ARP_PROBE);
|
||||
slaacv4_local.state = SLAACV4_CLAIMING;
|
||||
slaacv4_local.probe_try_nb++;
|
||||
slaacv4_local.timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, &slaacv4_local);
|
||||
if (!slaacv4_local.timer) {
|
||||
slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
|
||||
slaacv4_local.state = SLAACV4_ERROR;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pico_slaacv4_unregisterip(void)
|
||||
{
|
||||
struct slaacv4_cookie *tmp = &slaacv4_local;
|
||||
struct pico_ip4 empty = {
|
||||
.addr = 0x00000000
|
||||
};
|
||||
|
||||
if (tmp->state == SLAACV4_CLAIMED)
|
||||
{
|
||||
pico_ipv4_link_del(tmp->device, tmp->ip);
|
||||
}
|
||||
|
||||
pico_slaacv4_cancel_timers(tmp);
|
||||
pico_slaacv4_init_cookie(&empty, NULL, tmp, NULL);
|
||||
pico_arp_register_ipconflict(&tmp->ip, NULL, NULL);
|
||||
pico_hotplug_deregister(tmp->device, &pico_slaacv4_hotplug_cb);
|
||||
}
|
||||
|
||||
#endif
|
||||
18
kernel/picotcp/modules/pico_slaacv4.h
Normal file
18
kernel/picotcp/modules/pico_slaacv4.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Authors: Bogdan Lupu
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_SUPPORT_SLAACV4
|
||||
#define INCLUDE_PICO_SUPPORT_SLAACV4
|
||||
#include "pico_arp.h"
|
||||
|
||||
#define PICO_SLAACV4_SUCCESS 0
|
||||
#define PICO_SLAACV4_ERROR 1
|
||||
|
||||
int pico_slaacv4_claimip(struct pico_device *dev, void (*cb)(struct pico_ip4 *ip, uint8_t code));
|
||||
void pico_slaacv4_unregisterip(void);
|
||||
|
||||
#endif /* _INCLUDE_PICO_SUPPORT_SLAACV4 */
|
||||
|
||||
552
kernel/picotcp/modules/pico_sntp_client.c
Normal file
552
kernel/picotcp/modules/pico_sntp_client.c
Normal file
@ -0,0 +1,552 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Author: Toon Stegen
|
||||
*********************************************************************/
|
||||
#include "pico_sntp_client.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_dns_client.h"
|
||||
#include "pico_tree.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_SNTP_CLIENT
|
||||
|
||||
#ifdef DEBUG_SNTP
|
||||
#define sntp_dbg dbg
|
||||
#else
|
||||
#define sntp_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define SNTP_VERSION 4
|
||||
#define PICO_SNTP_MAXBUF (1400)
|
||||
|
||||
/* Sntp mode */
|
||||
#define SNTP_MODE_CLIENT 3
|
||||
|
||||
/* SNTP conversion parameters */
|
||||
#define SNTP_FRAC_TO_PICOSEC (4294967llu)
|
||||
#define SNTP_THOUSAND (1000llu)
|
||||
#define SNTP_UNIX_OFFSET (2208988800llu) /* nr of seconds from 1900 to 1970 */
|
||||
#define SNTP_BITMASK (0X00000000FFFFFFFF) /* mask to convert from 64 to 32 */
|
||||
|
||||
PACKED_STRUCT_DEF pico_sntp_ts
|
||||
{
|
||||
uint32_t sec; /* Seconds */
|
||||
uint32_t frac; /* Fraction */
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF pico_sntp_header
|
||||
{
|
||||
uint8_t mode : 3; /* Mode */
|
||||
uint8_t vn : 3; /* Version number */
|
||||
uint8_t li : 2; /* Leap indicator */
|
||||
uint8_t stratum; /* Stratum */
|
||||
uint8_t poll; /* Poll, only significant in server messages */
|
||||
uint8_t prec; /* Precision, only significant in server messages */
|
||||
int32_t rt_del; /* Root delay, only significant in server messages */
|
||||
int32_t rt_dis; /* Root dispersion, only significant in server messages */
|
||||
int32_t ref_id; /* Reference clock ID, only significant in server messages */
|
||||
struct pico_sntp_ts ref_ts; /* Reference time stamp */
|
||||
struct pico_sntp_ts orig_ts; /* Originate time stamp */
|
||||
struct pico_sntp_ts recv_ts; /* Receive time stamp */
|
||||
struct pico_sntp_ts trs_ts; /* Transmit time stamp */
|
||||
|
||||
};
|
||||
|
||||
struct sntp_server_ns_cookie
|
||||
{
|
||||
int rec; /* Indicates wheter an sntp packet has been received */
|
||||
uint16_t proto; /* IPV4 or IPV6 prototype */
|
||||
pico_time stamp; /* Timestamp of the moment the sntp packet is sent */
|
||||
char *hostname; /* Hostname of the (s)ntp server*/
|
||||
struct pico_socket *sock; /* Socket which contains the cookie */
|
||||
void (*cb_synced)(pico_err_t status); /* Callback function for telling the user
|
||||
wheter/when the time is synchronised */
|
||||
uint32_t timer; /* Timer that will signal timeout */
|
||||
};
|
||||
|
||||
/* global variables */
|
||||
static uint16_t sntp_port = 123u;
|
||||
static struct pico_timeval server_time = {
|
||||
0
|
||||
};
|
||||
static pico_time tick_stamp = 0ull;
|
||||
static union pico_address sntp_inaddr_any = {
|
||||
.ip6.addr = { 0 }
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* Converts a sntp time stamp to a pico_timeval struct */
|
||||
static int timestamp_convert(const struct pico_sntp_ts *ts, struct pico_timeval *tv, pico_time delay)
|
||||
{
|
||||
if(long_be(ts->sec) < SNTP_UNIX_OFFSET) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
tv->tv_sec = 0;
|
||||
tv->tv_msec = 0;
|
||||
sntp_dbg("Error: input too low\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sntp_dbg("Delay: %lu\n", delay);
|
||||
tv->tv_msec = (pico_time) (((uint32_t)(long_be(ts->frac))) / SNTP_FRAC_TO_PICOSEC + delay);
|
||||
tv->tv_sec = (pico_time) (long_be(ts->sec) - SNTP_UNIX_OFFSET + (uint32_t)tv->tv_msec / SNTP_THOUSAND);
|
||||
tv->tv_msec = (uint32_t) (tv->tv_msec & SNTP_BITMASK) % SNTP_THOUSAND;
|
||||
sntp_dbg("Converted time stamp: %lusec, %lumsec\n", tv->tv_sec, tv->tv_msec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup function that is called when the time is synced or an error occured */
|
||||
static void pico_sntp_cleanup(struct sntp_server_ns_cookie *ck, pico_err_t status)
|
||||
{
|
||||
sntp_dbg("Cleanup called\n");
|
||||
if(!ck)
|
||||
return;
|
||||
|
||||
pico_timer_cancel(ck->timer);
|
||||
|
||||
ck->cb_synced(status);
|
||||
if(ck->sock)
|
||||
ck->sock->priv = NULL;
|
||||
|
||||
sntp_dbg("FREE!\n");
|
||||
PICO_FREE(ck->hostname);
|
||||
PICO_FREE(ck);
|
||||
|
||||
}
|
||||
|
||||
/* Extracts the current time from a server sntp packet*/
|
||||
static int pico_sntp_parse(char *buf, struct sntp_server_ns_cookie *ck)
|
||||
{
|
||||
int ret = 0;
|
||||
struct pico_sntp_header *hp = (struct pico_sntp_header*) buf;
|
||||
|
||||
if(!ck) {
|
||||
sntp_dbg("pico_sntp_parse: invalid cookie\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sntp_dbg("Received mode: %u, version: %u, stratum: %u\n", hp->mode, hp->vn, hp->stratum);
|
||||
|
||||
tick_stamp = pico_tick;
|
||||
/* tick_stamp - ck->stamp is the delay between sending and receiving the ntp packet */
|
||||
ret = timestamp_convert(&(hp->trs_ts), &server_time, (tick_stamp - ck->stamp) / 2);
|
||||
if(ret != 0) {
|
||||
sntp_dbg("Conversion error!\n");
|
||||
pico_sntp_cleanup(ck, PICO_ERR_EINVAL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sntp_dbg("Server time: %lu seconds and %lu milisecs since 1970\n", server_time.tv_sec, server_time.tv_msec);
|
||||
|
||||
/* Call back the user saying the time is synced */
|
||||
pico_sntp_cleanup(ck, PICO_ERR_NOERR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* callback for UDP socket events */
|
||||
static void pico_sntp_client_wakeup(uint16_t ev, struct pico_socket *s)
|
||||
{
|
||||
struct sntp_server_ns_cookie *ck = (struct sntp_server_ns_cookie *)s->priv;
|
||||
char *recvbuf;
|
||||
int read = 0;
|
||||
uint32_t peer;
|
||||
uint16_t port;
|
||||
|
||||
if(!ck) {
|
||||
sntp_dbg("pico_sntp_client_wakeup: invalid cookie\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* process read event, data available */
|
||||
if (ev == PICO_SOCK_EV_RD) {
|
||||
ck->rec = 1;
|
||||
/* receive while data available in socket buffer */
|
||||
recvbuf = PICO_ZALLOC(PICO_SNTP_MAXBUF);
|
||||
if (!recvbuf)
|
||||
return;
|
||||
|
||||
do {
|
||||
read = pico_socket_recvfrom(s, recvbuf, PICO_SNTP_MAXBUF, &peer, &port);
|
||||
} while(read > 0);
|
||||
pico_sntp_parse(recvbuf, s->priv);
|
||||
s->priv = NULL; /* make sure UDP callback does not try to read from freed mem again */
|
||||
PICO_FREE(recvbuf);
|
||||
}
|
||||
/* socket is closed */
|
||||
else if(ev == PICO_SOCK_EV_CLOSE) {
|
||||
sntp_dbg("Socket is closed. Bailing out.\n");
|
||||
pico_sntp_cleanup(ck, PICO_ERR_ENOTCONN);
|
||||
return;
|
||||
}
|
||||
/* process error event, socket error occured */
|
||||
else if(ev == PICO_SOCK_EV_ERR) {
|
||||
sntp_dbg("Socket Error received. Bailing out.\n");
|
||||
pico_sntp_cleanup(ck, PICO_ERR_ENOTCONN);
|
||||
return;
|
||||
}
|
||||
|
||||
sntp_dbg("Received data from %08X:%u\n", peer, port);
|
||||
}
|
||||
|
||||
/* Function that is called after the receive timer expires */
|
||||
static void sntp_receive_timeout(pico_time now, void *arg)
|
||||
{
|
||||
struct sntp_server_ns_cookie *ck = (struct sntp_server_ns_cookie *)arg;
|
||||
(void) now;
|
||||
|
||||
if(!ck) {
|
||||
sntp_dbg("sntp_timeout: invalid cookie\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ck->rec) {
|
||||
pico_sntp_cleanup(ck, PICO_ERR_ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sends an sntp packet on sock to dst*/
|
||||
static void pico_sntp_send(struct pico_socket *sock, union pico_address *dst)
|
||||
{
|
||||
struct pico_sntp_header header = {
|
||||
0
|
||||
};
|
||||
struct sntp_server_ns_cookie *ck = (struct sntp_server_ns_cookie *)sock->priv;
|
||||
|
||||
if(!ck) {
|
||||
sntp_dbg("pico_sntp_sent: invalid cookie\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ck->timer = pico_timer_add(5000, sntp_receive_timeout, ck);
|
||||
if (!ck->timer) {
|
||||
sntp_dbg("SNTP: Failed to start timeout timer\n");
|
||||
pico_sntp_cleanup(ck, pico_err);
|
||||
pico_socket_close(sock);
|
||||
pico_socket_del(sock);
|
||||
return;
|
||||
}
|
||||
header.vn = SNTP_VERSION;
|
||||
header.mode = SNTP_MODE_CLIENT;
|
||||
/* header.trs_ts.frac = long_be(0ul); */
|
||||
ck->stamp = pico_tick;
|
||||
pico_socket_sendto(sock, &header, sizeof(header), dst, short_be(sntp_port));
|
||||
}
|
||||
|
||||
static int pico_sntp_sync_start(struct sntp_server_ns_cookie *ck, union pico_address *addr)
|
||||
{
|
||||
uint16_t any_port = 0;
|
||||
struct pico_socket *sock;
|
||||
|
||||
sock = pico_socket_open(ck->proto, PICO_PROTO_UDP, &pico_sntp_client_wakeup);
|
||||
if (!sock)
|
||||
return -1;
|
||||
|
||||
sock->priv = ck;
|
||||
ck->sock = sock;
|
||||
if ((pico_socket_bind(sock, &sntp_inaddr_any, &any_port) < 0)) {
|
||||
pico_socket_close(sock);
|
||||
return -1;
|
||||
}
|
||||
pico_sntp_send(sock, addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_DNS_CLIENT
|
||||
/* used for getting a response from DNS servers */
|
||||
static void dnsCallback(char *ip, void *arg)
|
||||
{
|
||||
struct sntp_server_ns_cookie *ck = (struct sntp_server_ns_cookie *)arg;
|
||||
union pico_address address;
|
||||
int retval = -1;
|
||||
|
||||
if(!ck) {
|
||||
sntp_dbg("dnsCallback: Invalid argument\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(ck->proto == PICO_PROTO_IPV6) {
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
if (ip) {
|
||||
/* add the ip address to the client, and start a tcp connection socket */
|
||||
sntp_dbg("using IPv6 address: %s\n", ip);
|
||||
retval = pico_string_to_ipv6(ip, address.ip6.addr);
|
||||
} else {
|
||||
sntp_dbg("Invalid query response for AAAA\n");
|
||||
retval = -1;
|
||||
pico_sntp_cleanup(ck, PICO_ERR_ENETDOWN);
|
||||
}
|
||||
#endif
|
||||
} else if(ck->proto == PICO_PROTO_IPV4) {
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
if(ip) {
|
||||
sntp_dbg("using IPv4 address: %s\n", ip);
|
||||
retval = pico_string_to_ipv4(ip, (uint32_t *)&address.ip4.addr);
|
||||
} else {
|
||||
sntp_dbg("Invalid query response for A\n");
|
||||
retval = -1;
|
||||
pico_sntp_cleanup(ck, PICO_ERR_ENETDOWN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (retval >= 0) {
|
||||
retval = pico_sntp_sync_start(ck, &address);
|
||||
if (retval < 0)
|
||||
pico_sntp_cleanup(ck, PICO_ERR_ENOTCONN);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
#ifdef PICO_SUPPORT_DNS_CLIENT
|
||||
static int pico_sntp_sync_start_dns_ipv4(const char *sntp_server, void (*cb_synced)(pico_err_t status))
|
||||
{
|
||||
int retval = -1;
|
||||
struct sntp_server_ns_cookie *ck;
|
||||
/* IPv4 query */
|
||||
ck = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
|
||||
if (!ck) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ck->proto = PICO_PROTO_IPV4;
|
||||
ck->stamp = 0ull;
|
||||
ck->rec = 0;
|
||||
ck->sock = NULL;
|
||||
ck->hostname = PICO_ZALLOC(strlen(sntp_server) + 1);
|
||||
if (!ck->hostname) {
|
||||
PICO_FREE(ck);
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(ck->hostname, sntp_server);
|
||||
|
||||
ck->cb_synced = cb_synced;
|
||||
|
||||
sntp_dbg("Resolving A %s\n", ck->hostname);
|
||||
retval = pico_dns_client_getaddr(sntp_server, &dnsCallback, ck);
|
||||
if (retval != 0) {
|
||||
PICO_FREE(ck->hostname);
|
||||
PICO_FREE(ck);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static int pico_sntp_sync_start_ipv4(union pico_address *addr, void (*cb_synced)(pico_err_t status))
|
||||
{
|
||||
int retval = -1;
|
||||
struct sntp_server_ns_cookie *ck;
|
||||
ck = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
|
||||
if (!ck) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ck->proto = PICO_PROTO_IPV4;
|
||||
ck->stamp = 0ull;
|
||||
ck->rec = 0;
|
||||
ck->sock = NULL;
|
||||
/* Set the given IP address as hostname, allocate the maximum IPv4 string length + 1 */
|
||||
ck->hostname = PICO_ZALLOC(15 + 1);
|
||||
if (!ck->hostname) {
|
||||
PICO_FREE(ck);
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
retval = pico_ipv4_to_string(ck->hostname, addr->ip4.addr);
|
||||
if (retval < 0) {
|
||||
PICO_FREE(ck->hostname);
|
||||
PICO_FREE(ck);
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ck->cb_synced = cb_synced;
|
||||
|
||||
retval = pico_sntp_sync_start(ck, addr);
|
||||
if (retval < 0) {
|
||||
pico_sntp_cleanup(ck, PICO_ERR_ENOTCONN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
#ifdef PICO_SUPPORT_DNS_CLIENT
|
||||
static int pico_sntp_sync_start_dns_ipv6(const char *sntp_server, void (*cb_synced)(pico_err_t status))
|
||||
{
|
||||
struct sntp_server_ns_cookie *ck6;
|
||||
int retval6 = -1;
|
||||
/* IPv6 query */
|
||||
ck6 = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
|
||||
if (!ck6) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ck6->proto = PICO_PROTO_IPV6;
|
||||
ck6->hostname = PICO_ZALLOC(strlen(sntp_server) + 1);
|
||||
if (!ck6->hostname) {
|
||||
PICO_FREE(ck6);
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(ck6->hostname, sntp_server);
|
||||
ck6->proto = PICO_PROTO_IPV6;
|
||||
ck6->stamp = 0ull;
|
||||
ck6->rec = 0;
|
||||
ck6->sock = NULL;
|
||||
ck6->cb_synced = cb_synced;
|
||||
sntp_dbg("Resolving AAAA %s\n", ck6->hostname);
|
||||
retval6 = pico_dns_client_getaddr6(sntp_server, &dnsCallback, ck6);
|
||||
if (retval6 != 0) {
|
||||
PICO_FREE(ck6->hostname);
|
||||
PICO_FREE(ck6);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static int pico_sntp_sync_start_ipv6(union pico_address *addr, void (*cb_synced)(pico_err_t status))
|
||||
{
|
||||
struct sntp_server_ns_cookie *ck6;
|
||||
int retval6 = -1;
|
||||
ck6 = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
|
||||
if (!ck6) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ck6->proto = PICO_PROTO_IPV6;
|
||||
ck6->stamp = 0ull;
|
||||
ck6->rec = 0;
|
||||
ck6->sock = NULL;
|
||||
ck6->cb_synced = cb_synced;
|
||||
/* Set the given IP address as hostname, allocate the maximum IPv6 string length + 1 */
|
||||
ck6->hostname = PICO_ZALLOC(39 + 1);
|
||||
if (!ck6->hostname) {
|
||||
PICO_FREE(ck6);
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
retval6 = pico_ipv6_to_string(ck6->hostname, addr->ip6.addr);
|
||||
if (retval6 < 0) {
|
||||
PICO_FREE(ck6->hostname);
|
||||
PICO_FREE(ck6);
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
retval6 = pico_sntp_sync_start(ck6, addr);
|
||||
if (retval6 < 0) {
|
||||
pico_sntp_cleanup(ck6, PICO_ERR_ENOTCONN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* user function to sync the time from a given sntp source in string notation, DNS resolution is needed */
|
||||
int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status))
|
||||
{
|
||||
#ifdef PICO_SUPPORT_DNS_CLIENT
|
||||
int retval4 = -1, retval6 = -1;
|
||||
if (sntp_server == NULL) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(cb_synced == NULL) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
retval4 = pico_sntp_sync_start_dns_ipv4(sntp_server, cb_synced);
|
||||
#endif
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
retval6 = pico_sntp_sync_start_dns_ipv6(sntp_server, cb_synced);
|
||||
#endif
|
||||
|
||||
if (retval4 != 0 && retval6 != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
sntp_debug("No DNS support available\n");
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* user function to sync the time from a given sntp source in pico_address notation */
|
||||
int pico_sntp_sync_ip(union pico_address *sntp_addr, void (*cb_synced)(pico_err_t status))
|
||||
{
|
||||
int retval4 = -1, retval6 = -1;
|
||||
if (sntp_addr == NULL) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cb_synced == NULL) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
retval4 = pico_sntp_sync_start_ipv4(sntp_addr, cb_synced);
|
||||
#endif
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
retval6 = pico_sntp_sync_start_ipv6(sntp_addr, cb_synced);
|
||||
#endif
|
||||
|
||||
if (retval4 != 0 && retval6 != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* user function to get the current time */
|
||||
int pico_sntp_gettimeofday(struct pico_timeval *tv)
|
||||
{
|
||||
pico_time diff, temp;
|
||||
uint32_t diffH, diffL;
|
||||
int ret = 0;
|
||||
if (tick_stamp == 0) {
|
||||
/* TODO: set pico_err */
|
||||
ret = -1;
|
||||
sntp_dbg("Error: Unsynchronised\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
diff = pico_tick - tick_stamp;
|
||||
diffL = ((uint32_t) (diff & SNTP_BITMASK)) / 1000;
|
||||
diffH = ((uint32_t) (diff >> 32)) / 1000;
|
||||
|
||||
temp = server_time.tv_msec + (uint32_t)(diff & SNTP_BITMASK) % SNTP_THOUSAND;
|
||||
tv->tv_sec = server_time.tv_sec + ((uint64_t)diffH << 32) + diffL + (uint32_t)temp / SNTP_THOUSAND;
|
||||
tv->tv_msec = (uint32_t)(temp & SNTP_BITMASK) % SNTP_THOUSAND;
|
||||
sntp_dbg("Time of day: %lu seconds and %lu milisecs since 1970\n", tv->tv_sec, tv->tv_msec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* PICO_SUPPORT_SNTP_CLIENT */
|
||||
23
kernel/picotcp/modules/pico_sntp_client.h
Normal file
23
kernel/picotcp/modules/pico_sntp_client.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
Author: Toon Stegen
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_SNTP_CLIENT
|
||||
#define INCLUDE_PICO_SNTP_CLIENT
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_protocol.h"
|
||||
|
||||
struct pico_timeval
|
||||
{
|
||||
pico_time tv_sec;
|
||||
pico_time tv_msec;
|
||||
};
|
||||
|
||||
int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status));
|
||||
int pico_sntp_sync_ip(union pico_address *sntp_addr, void (*cb_synced)(pico_err_t status));
|
||||
int pico_sntp_gettimeofday(struct pico_timeval *tv);
|
||||
|
||||
#endif /* _INCLUDE_PICO_SNTP_CLIENT */
|
||||
272
kernel/picotcp/modules/pico_socket_tcp.c
Normal file
272
kernel/picotcp/modules/pico_socket_tcp.c
Normal file
@ -0,0 +1,272 @@
|
||||
#include "pico_config.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_tcp.h"
|
||||
#include "pico_socket_tcp.h"
|
||||
|
||||
|
||||
static int sockopt_validate_args(struct pico_socket *s, void *value)
|
||||
{
|
||||
if (!value) {
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->proto->proto_number != PICO_PROTO_TCP) {
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value)
|
||||
{
|
||||
if (sockopt_validate_args(s, value) < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
if (option == PICO_TCP_NODELAY) {
|
||||
/* state of the NODELAY option */
|
||||
*(int *)value = PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_TCPNODELAY);
|
||||
return 0;
|
||||
}
|
||||
else if (option == PICO_SOCKET_OPT_RCVBUF) {
|
||||
return pico_tcp_get_bufsize_in(s, (uint32_t *)value);
|
||||
}
|
||||
|
||||
else if (option == PICO_SOCKET_OPT_SNDBUF) {
|
||||
return pico_tcp_get_bufsize_out(s, (uint32_t *)value);
|
||||
}
|
||||
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void tcp_set_nagle_option(struct pico_socket *s, void *value)
|
||||
{
|
||||
int *val = (int*)value;
|
||||
if (*val > 0) {
|
||||
dbg("setsockopt: Nagle algorithm disabled.\n");
|
||||
PICO_SOCKET_SETOPT_EN(s, PICO_SOCKET_OPT_TCPNODELAY);
|
||||
} else {
|
||||
dbg("setsockopt: Nagle algorithm enabled.\n");
|
||||
PICO_SOCKET_SETOPT_DIS(s, PICO_SOCKET_OPT_TCPNODELAY);
|
||||
}
|
||||
}
|
||||
|
||||
int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value)
|
||||
{
|
||||
if (sockopt_validate_args(s, value) < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
if (option == PICO_TCP_NODELAY) {
|
||||
tcp_set_nagle_option(s, value);
|
||||
return 0;
|
||||
}
|
||||
else if (option == PICO_SOCKET_OPT_RCVBUF) {
|
||||
uint32_t *val = (uint32_t*)value;
|
||||
pico_tcp_set_bufsize_in(s, *val);
|
||||
return 0;
|
||||
}
|
||||
else if (option == PICO_SOCKET_OPT_SNDBUF) {
|
||||
uint32_t *val = (uint32_t*)value;
|
||||
pico_tcp_set_bufsize_out(s, *val);
|
||||
return 0;
|
||||
}
|
||||
else if (option == PICO_SOCKET_OPT_KEEPCNT) {
|
||||
uint32_t *val = (uint32_t*)value;
|
||||
pico_tcp_set_keepalive_probes(s, *val);
|
||||
return 0;
|
||||
}
|
||||
else if (option == PICO_SOCKET_OPT_KEEPIDLE) {
|
||||
uint32_t *val = (uint32_t*)value;
|
||||
pico_tcp_set_keepalive_time(s, *val);
|
||||
return 0;
|
||||
}
|
||||
else if (option == PICO_SOCKET_OPT_KEEPINTVL) {
|
||||
uint32_t *val = (uint32_t*)value;
|
||||
pico_tcp_set_keepalive_intvl(s, *val);
|
||||
return 0;
|
||||
}
|
||||
else if (option == PICO_SOCKET_OPT_LINGER) {
|
||||
uint32_t *val = (uint32_t*)value;
|
||||
pico_tcp_set_linger(s, *val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pico_socket_tcp_cleanup(struct pico_socket *sock)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
/* for tcp sockets go further and clean the sockets inside queue */
|
||||
if(is_sock_tcp(sock))
|
||||
pico_tcp_cleanup_queues(sock);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void pico_socket_tcp_delete(struct pico_socket *s)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
if(s->parent)
|
||||
s->parent->number_of_pending_conn--;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct pico_socket *socket_tcp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
struct pico_socket *found = NULL;
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
struct pico_ip4 s_local, s_remote, p_src, p_dst;
|
||||
struct pico_ipv4_hdr *ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
|
||||
struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
|
||||
s_local.addr = s->local_addr.ip4.addr;
|
||||
s_remote.addr = s->remote_addr.ip4.addr;
|
||||
p_src.addr = ip4hdr->src.addr;
|
||||
p_dst.addr = ip4hdr->dst.addr;
|
||||
if ((s->remote_port == tr->sport) && /* remote port check */
|
||||
(s_remote.addr == p_src.addr) && /* remote addr check */
|
||||
((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
|
||||
found = s;
|
||||
return found;
|
||||
} else if ((s->remote_port == 0) && /* not connected... listening */
|
||||
((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
|
||||
/* listen socket */
|
||||
found = s;
|
||||
}
|
||||
|
||||
#endif
|
||||
return found;
|
||||
}
|
||||
|
||||
static struct pico_socket *socket_tcp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
struct pico_socket *found = NULL;
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
|
||||
struct pico_ip6 s_local = {{0}}, s_remote = {{0}}, p_src = {{0}}, p_dst = {{0}};
|
||||
struct pico_ipv6_hdr *ip6hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
|
||||
s_local = s->local_addr.ip6;
|
||||
s_remote = s->remote_addr.ip6;
|
||||
p_src = ip6hdr->src;
|
||||
p_dst = ip6hdr->dst;
|
||||
if ((s->remote_port == tr->sport) &&
|
||||
(!memcmp(s_remote.addr, p_src.addr, PICO_SIZE_IP6)) &&
|
||||
((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
|
||||
found = s;
|
||||
return found;
|
||||
} else if ((s->remote_port == 0) && /* not connected... listening */
|
||||
((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
|
||||
/* listen socket */
|
||||
found = s;
|
||||
}
|
||||
|
||||
#else
|
||||
(void) s;
|
||||
(void) f;
|
||||
#endif
|
||||
return found;
|
||||
}
|
||||
|
||||
static int socket_tcp_do_deliver(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
if (s != NULL) {
|
||||
pico_tcp_input(s, f);
|
||||
if ((s->ev_pending) && s->wakeup) {
|
||||
s->wakeup(s->ev_pending, s);
|
||||
if(!s->parent)
|
||||
s->ev_pending = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbg("TCP SOCKET> Not s.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f)
|
||||
{
|
||||
struct pico_socket *found = NULL;
|
||||
struct pico_socket *target = NULL;
|
||||
struct pico_tree_node *index = NULL;
|
||||
struct pico_tree_node *_tmp;
|
||||
struct pico_socket *s = NULL;
|
||||
|
||||
pico_tree_foreach_safe(index, &sp->socks, _tmp){
|
||||
s = index->keyValue;
|
||||
/* 4-tuple identification of socket (port-IP) */
|
||||
if (IS_IPV4(f)) {
|
||||
found = socket_tcp_deliver_ipv4(s, f);
|
||||
}
|
||||
|
||||
if (IS_IPV6(f)) {
|
||||
found = socket_tcp_deliver_ipv6(s, f);
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
target = found;
|
||||
if ( found->remote_port != 0)
|
||||
/* only break if it's connected */
|
||||
break;
|
||||
}
|
||||
} /* FOREACH */
|
||||
|
||||
return socket_tcp_do_deliver(target, f);
|
||||
}
|
||||
|
||||
struct pico_socket *pico_socket_tcp_open(uint16_t family)
|
||||
{
|
||||
struct pico_socket *s = NULL;
|
||||
(void) family;
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
s = pico_tcp_open(family);
|
||||
if (!s) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->proto = &pico_proto_tcp;
|
||||
/*check if Nagle enabled */
|
||||
/*
|
||||
if (!IS_NAGLE_ENABLED(s))
|
||||
dbg("ERROR Nagle should be enabled here\n\n");
|
||||
*/
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
int pico_socket_tcp_read(struct pico_socket *s, void *buf, uint32_t len)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
/* check if in shutdown state and if no more data in tcpq_in */
|
||||
if ((s->state & PICO_SOCKET_STATE_SHUT_REMOTE) && pico_tcp_queue_in_is_empty(s)) {
|
||||
pico_err = PICO_ERR_ESHUTDOWN;
|
||||
return -1;
|
||||
} else {
|
||||
return (int)(pico_tcp_read(s, buf, (uint32_t)len));
|
||||
}
|
||||
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void transport_flags_update(struct pico_frame *f, struct pico_socket *s)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
if(is_sock_tcp(s))
|
||||
pico_tcp_flags_update(f, s);
|
||||
|
||||
#endif
|
||||
}
|
||||
33
kernel/picotcp/modules/pico_socket_tcp.h
Normal file
33
kernel/picotcp/modules/pico_socket_tcp.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef PICO_SOCKET_TCP_H
|
||||
#define PICO_SOCKET_TCP_H
|
||||
#include "pico_socket.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
|
||||
/* Functions/macros: conditional! */
|
||||
|
||||
# define IS_NAGLE_ENABLED(s) (!(!(!(s->opt_flags & (1 << PICO_SOCKET_OPT_TCPNODELAY)))))
|
||||
int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value);
|
||||
int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value);
|
||||
int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f);
|
||||
void pico_socket_tcp_delete(struct pico_socket *s);
|
||||
void pico_socket_tcp_cleanup(struct pico_socket *sock);
|
||||
struct pico_socket *pico_socket_tcp_open(uint16_t family);
|
||||
int pico_socket_tcp_read(struct pico_socket *s, void *buf, uint32_t len);
|
||||
void transport_flags_update(struct pico_frame *, struct pico_socket *);
|
||||
|
||||
#else
|
||||
# define pico_getsockopt_tcp(...) (-1)
|
||||
# define pico_setsockopt_tcp(...) (-1)
|
||||
# define pico_socket_tcp_deliver(...) (-1)
|
||||
# define IS_NAGLE_ENABLED(s) (0)
|
||||
# define pico_socket_tcp_delete(...) do {} while(0)
|
||||
# define pico_socket_tcp_cleanup(...) do {} while(0)
|
||||
# define pico_socket_tcp_open(f) (NULL)
|
||||
# define pico_socket_tcp_read(...) (-1)
|
||||
# define transport_flags_update(...) do {} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
260
kernel/picotcp/modules/pico_socket_udp.c
Normal file
260
kernel/picotcp/modules/pico_socket_udp.c
Normal file
@ -0,0 +1,260 @@
|
||||
#include "pico_config.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_udp.h"
|
||||
#include "pico_socket_multicast.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_socket_udp.h"
|
||||
|
||||
#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
|
||||
|
||||
|
||||
struct pico_socket *pico_socket_udp_open(void)
|
||||
{
|
||||
struct pico_socket *s = NULL;
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
s = pico_udp_open();
|
||||
if (!s) {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->proto = &pico_proto_udp;
|
||||
s->q_in.overhead = UDP_FRAME_OVERHEAD;
|
||||
s->q_out.overhead = UDP_FRAME_OVERHEAD;
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6)
|
||||
static int pico_enqueue_and_wakeup_if_needed(struct pico_queue *q_in, struct pico_socket* s, struct pico_frame* cpy)
|
||||
{
|
||||
if (pico_enqueue(q_in, cpy) > 0) {
|
||||
if (s->wakeup){
|
||||
s->wakeup(PICO_SOCK_EV_RD, s);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pico_frame_discard(cpy);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
static inline int pico_socket_udp_deliver_ipv4_mcast_initial_checks(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
struct pico_ip4 p_dst;
|
||||
struct pico_ipv4_hdr *ip4hdr;
|
||||
|
||||
ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
|
||||
p_dst.addr = ip4hdr->dst.addr;
|
||||
if (pico_ipv4_is_multicast(p_dst.addr) && (pico_socket_mcast_filter(s, (union pico_address *)&ip4hdr->dst, (union pico_address *)&ip4hdr->src) < 0))
|
||||
return -1;
|
||||
|
||||
|
||||
if ((pico_ipv4_link_get(&ip4hdr->src)) && (PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP) == 0u)) {
|
||||
/* Datagram from ourselves, Loop disabled, discarding. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int pico_socket_udp_deliver_ipv4_mcast(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
struct pico_ip4 s_local;
|
||||
struct pico_frame *cpy;
|
||||
struct pico_device *dev = pico_ipv4_link_find(&s->local_addr.ip4);
|
||||
|
||||
s_local.addr = s->local_addr.ip4.addr;
|
||||
|
||||
if (pico_socket_udp_deliver_ipv4_mcast_initial_checks(s, f) < 0)
|
||||
return 0;
|
||||
|
||||
if ((s_local.addr == PICO_IPV4_INADDR_ANY) || /* If our local ip is ANY, or.. */
|
||||
(dev == f->dev)) { /* the source of the bcast packet is a neighbor... */
|
||||
cpy = pico_frame_copy(f);
|
||||
if (!cpy)
|
||||
return -1;
|
||||
|
||||
pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static int pico_socket_udp_deliver_ipv4_unicast(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
struct pico_frame *cpy;
|
||||
/* Either local socket is ANY, or matches dst */
|
||||
cpy = pico_frame_copy(f);
|
||||
if (!cpy)
|
||||
return -1;
|
||||
|
||||
pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int pico_socket_udp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
int ret = 0;
|
||||
struct pico_ip4 s_local, p_dst;
|
||||
struct pico_ipv4_hdr *ip4hdr;
|
||||
ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
|
||||
s_local.addr = s->local_addr.ip4.addr;
|
||||
p_dst.addr = ip4hdr->dst.addr;
|
||||
if ((pico_ipv4_is_broadcast(p_dst.addr)) || pico_ipv4_is_multicast(p_dst.addr)) {
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
ret = pico_socket_udp_deliver_ipv4_mcast(s, f);
|
||||
#endif
|
||||
} else if ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr)) {
|
||||
ret = pico_socket_udp_deliver_ipv4_unicast(s, f);
|
||||
}
|
||||
|
||||
pico_frame_discard(f);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
static inline int pico_socket_udp_deliver_ipv6_mcast(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *ip6hdr;
|
||||
struct pico_frame *cpy;
|
||||
struct pico_device *dev = pico_ipv6_link_find(&s->local_addr.ip6);
|
||||
|
||||
ip6hdr = (struct pico_ipv6_hdr*)(f->net_hdr);
|
||||
|
||||
if ((pico_ipv6_link_get(&ip6hdr->src)) && (PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP) == 0u)) {
|
||||
/* Datagram from ourselves, Loop disabled, discarding. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || /* If our local ip is ANY, or.. */
|
||||
(dev == f->dev)) { /* the source of the bcast packet is a neighbor... */
|
||||
cpy = pico_frame_copy(f);
|
||||
if (!cpy)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static int pico_socket_udp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f)
|
||||
{
|
||||
struct pico_ip6 s_local, p_dst;
|
||||
struct pico_ipv6_hdr *ip6hdr;
|
||||
struct pico_frame *cpy;
|
||||
ip6hdr = (struct pico_ipv6_hdr*)(f->net_hdr);
|
||||
s_local = s->local_addr.ip6;
|
||||
p_dst = ip6hdr->dst;
|
||||
if ((pico_ipv6_is_multicast(p_dst.addr))) {
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
int retval = pico_socket_udp_deliver_ipv6_mcast(s, f);
|
||||
pico_frame_discard(f);
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
else if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || (pico_ipv6_compare(&s_local, &p_dst) == 0))
|
||||
{ /* Either local socket is ANY, or matches dst */
|
||||
cpy = pico_frame_copy(f);
|
||||
if (!cpy)
|
||||
{
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
|
||||
}
|
||||
|
||||
pico_frame_discard(f);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f)
|
||||
{
|
||||
struct pico_tree_node *index = NULL;
|
||||
struct pico_tree_node *_tmp;
|
||||
struct pico_socket *s = NULL;
|
||||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
pico_err = PICO_ERR_NOERR;
|
||||
pico_tree_foreach_safe(index, &sp->socks, _tmp){
|
||||
s = index->keyValue;
|
||||
if (IS_IPV4(f)) { /* IPV4 */
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
return pico_socket_udp_deliver_ipv4(s, f);
|
||||
#endif
|
||||
} else if (IS_IPV6(f)) {
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
return pico_socket_udp_deliver_ipv6(s, f);
|
||||
#endif
|
||||
} else {
|
||||
/* something wrong in the packet header*/
|
||||
}
|
||||
} /* FOREACH */
|
||||
pico_frame_discard(f);
|
||||
if (s)
|
||||
return 0;
|
||||
|
||||
pico_err = PICO_ERR_ENXIO;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pico_setsockopt_udp(struct pico_socket *s, int option, void *value)
|
||||
{
|
||||
switch(option) {
|
||||
case PICO_SOCKET_OPT_RCVBUF:
|
||||
s->q_in.max_size = (*(uint32_t*)value);
|
||||
return 0;
|
||||
case PICO_SOCKET_OPT_SNDBUF:
|
||||
s->q_out.max_size = (*(uint32_t*)value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* switch's default */
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
return pico_setsockopt_mcast(s, option, value);
|
||||
#else
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int pico_getsockopt_udp(struct pico_socket *s, int option, void *value)
|
||||
{
|
||||
uint32_t *val = (uint32_t *)value;
|
||||
switch(option) {
|
||||
case PICO_SOCKET_OPT_RCVBUF:
|
||||
*val = s->q_in.max_size;
|
||||
return 0;
|
||||
case PICO_SOCKET_OPT_SNDBUF:
|
||||
*val = s->q_out.max_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* switch's default */
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
return pico_getsockopt_mcast(s, option, value);
|
||||
#else
|
||||
pico_err = PICO_ERR_EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
19
kernel/picotcp/modules/pico_socket_udp.h
Normal file
19
kernel/picotcp/modules/pico_socket_udp.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef PICO_SOCKET_UDP_H
|
||||
#define PICO_SOCKET_UDP_H
|
||||
|
||||
struct pico_socket *pico_socket_udp_open(void);
|
||||
int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f);
|
||||
|
||||
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
int pico_setsockopt_udp(struct pico_socket *s, int option, void *value);
|
||||
int pico_getsockopt_udp(struct pico_socket *s, int option, void *value);
|
||||
# define pico_socket_udp_recv(s, buf, len, addr, port) pico_udp_recv(s, buf, len, addr, port, NULL)
|
||||
#else
|
||||
# define pico_socket_udp_recv(...) (0)
|
||||
# define pico_getsockopt_udp(...) (-1)
|
||||
# define pico_setsockopt_udp(...) (-1)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
101
kernel/picotcp/modules/pico_strings.c
Normal file
101
kernel/picotcp/modules/pico_strings.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2015-2017 Altran ISY BeNeLux. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
|
||||
|
||||
Author: Michele Di Pede
|
||||
*********************************************************************/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include "pico_strings.h"
|
||||
|
||||
char *get_string_terminator_position(char *const block, size_t len)
|
||||
{
|
||||
size_t length = pico_strnlen(block, len);
|
||||
|
||||
return (len != length) ? (block + length) : 0;
|
||||
}
|
||||
|
||||
int pico_strncasecmp(const char *const str1, const char *const str2, size_t n)
|
||||
{
|
||||
int ch1;
|
||||
int ch2;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
ch1 = toupper(*(str1 + i));
|
||||
ch2 = toupper(*(str2 + i));
|
||||
if (ch1 < ch2)
|
||||
return -1;
|
||||
|
||||
if (ch1 > ch2)
|
||||
return 1;
|
||||
|
||||
if ((!ch1) && (!ch2))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t pico_strnlen(const char *str, size_t n)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
for (; len < n && *(str + len); ++len)
|
||||
; /* TICS require this empty statement here */
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline int num2string_validate(int32_t num, char *buf, int len)
|
||||
{
|
||||
if (num < 0)
|
||||
return -1;
|
||||
|
||||
if (!buf)
|
||||
return -2;
|
||||
|
||||
if (len < 2)
|
||||
return -3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int revert_and_shift(char *buf, int len, int pos)
|
||||
{
|
||||
int i;
|
||||
|
||||
len -= pos;
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[i] = buf[i + pos];
|
||||
return len;
|
||||
}
|
||||
|
||||
int num2string(int32_t num, char *buf, int len)
|
||||
{
|
||||
ldiv_t res;
|
||||
int pos = 0;
|
||||
|
||||
if (num2string_validate(num, buf, len))
|
||||
return -1;
|
||||
|
||||
pos = len;
|
||||
buf[--pos] = '\0';
|
||||
|
||||
res.quot = (long)num;
|
||||
|
||||
do {
|
||||
if (!pos)
|
||||
return -3;
|
||||
|
||||
res = ldiv(res.quot, 10);
|
||||
buf[--pos] = (char)((res.rem + '0') & 0xFF);
|
||||
} while (res.quot);
|
||||
|
||||
return revert_and_shift(buf, len, pos);
|
||||
}
|
||||
21
kernel/picotcp/modules/pico_strings.h
Normal file
21
kernel/picotcp/modules/pico_strings.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2015-2017 Altran ISY BeNeLux. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Author: Michele Di Pede
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef PICO_STRINGS_H
|
||||
#define PICO_STRINGS_H
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
char *get_string_terminator_position(char *const block, size_t len);
|
||||
int pico_strncasecmp(const char *const str1, const char *const str2, size_t n);
|
||||
size_t pico_strnlen(const char *str, size_t n);
|
||||
|
||||
int num2string(int32_t num, char *buf, int len);
|
||||
|
||||
#endif
|
||||
3314
kernel/picotcp/modules/pico_tcp.c
Normal file
3314
kernel/picotcp/modules/pico_tcp.c
Normal file
File diff suppressed because it is too large
Load Diff
106
kernel/picotcp/modules/pico_tcp.h
Normal file
106
kernel/picotcp/modules/pico_tcp.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_TCP
|
||||
#define INCLUDE_PICO_TCP
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_socket.h"
|
||||
|
||||
extern struct pico_protocol pico_proto_tcp;
|
||||
|
||||
PACKED_STRUCT_DEF pico_tcp_hdr {
|
||||
struct pico_trans trans;
|
||||
uint32_t seq;
|
||||
uint32_t ack;
|
||||
uint8_t len;
|
||||
uint8_t flags;
|
||||
uint16_t rwnd;
|
||||
uint16_t crc;
|
||||
uint16_t urgent;
|
||||
};
|
||||
|
||||
PACKED_STRUCT_DEF tcp_pseudo_hdr_ipv4
|
||||
{
|
||||
struct pico_ip4 src;
|
||||
struct pico_ip4 dst;
|
||||
uint16_t tcp_len;
|
||||
uint8_t res;
|
||||
uint8_t proto;
|
||||
};
|
||||
|
||||
#define PICO_TCPHDR_SIZE 20
|
||||
#define PICO_SIZE_TCPOPT_SYN 20
|
||||
#define PICO_SIZE_TCPHDR (uint32_t)(sizeof(struct pico_tcp_hdr))
|
||||
|
||||
/* TCP options */
|
||||
#define PICO_TCP_OPTION_END 0x00
|
||||
#define PICO_TCPOPTLEN_END 1u
|
||||
#define PICO_TCP_OPTION_NOOP 0x01
|
||||
#define PICO_TCPOPTLEN_NOOP 1
|
||||
#define PICO_TCP_OPTION_MSS 0x02
|
||||
#define PICO_TCPOPTLEN_MSS 4
|
||||
#define PICO_TCP_OPTION_WS 0x03
|
||||
#define PICO_TCPOPTLEN_WS 3u
|
||||
#define PICO_TCP_OPTION_SACK_OK 0x04
|
||||
#define PICO_TCPOPTLEN_SACK_OK 2
|
||||
#define PICO_TCP_OPTION_SACK 0x05
|
||||
#define PICO_TCPOPTLEN_SACK 2 /* Plus the block */
|
||||
#define PICO_TCP_OPTION_TIMESTAMP 0x08
|
||||
#define PICO_TCPOPTLEN_TIMESTAMP 10u
|
||||
|
||||
/* TCP flags */
|
||||
#define PICO_TCP_FIN 0x01u
|
||||
#define PICO_TCP_SYN 0x02u
|
||||
#define PICO_TCP_RST 0x04u
|
||||
#define PICO_TCP_PSH 0x08u
|
||||
#define PICO_TCP_ACK 0x10u
|
||||
#define PICO_TCP_URG 0x20u
|
||||
#define PICO_TCP_ECN 0x40u
|
||||
#define PICO_TCP_CWR 0x80u
|
||||
|
||||
#define PICO_TCP_SYNACK (PICO_TCP_SYN | PICO_TCP_ACK)
|
||||
#define PICO_TCP_PSHACK (PICO_TCP_PSH | PICO_TCP_ACK)
|
||||
#define PICO_TCP_FINACK (PICO_TCP_FIN | PICO_TCP_ACK)
|
||||
#define PICO_TCP_FINPSHACK (PICO_TCP_FIN | PICO_TCP_PSH | PICO_TCP_ACK)
|
||||
#define PICO_TCP_RSTACK (PICO_TCP_RST | PICO_TCP_ACK)
|
||||
|
||||
|
||||
PACKED_STRUCT_DEF pico_tcp_option
|
||||
{
|
||||
uint8_t kind;
|
||||
uint8_t len;
|
||||
};
|
||||
|
||||
struct pico_socket *pico_tcp_open(uint16_t family);
|
||||
uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len);
|
||||
int pico_tcp_initconn(struct pico_socket *s);
|
||||
int pico_tcp_input(struct pico_socket *s, struct pico_frame *f);
|
||||
uint16_t pico_tcp_checksum(struct pico_frame *f);
|
||||
uint16_t pico_tcp_checksum_ipv4(struct pico_frame *f);
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
uint16_t pico_tcp_checksum_ipv6(struct pico_frame *f);
|
||||
#endif
|
||||
uint16_t pico_tcp_overhead(struct pico_socket *s);
|
||||
int pico_tcp_output(struct pico_socket *s, int loop_score);
|
||||
int pico_tcp_queue_in_is_empty(struct pico_socket *s);
|
||||
int pico_tcp_reply_rst(struct pico_frame *f);
|
||||
void pico_tcp_cleanup_queues(struct pico_socket *sck);
|
||||
void pico_tcp_notify_closing(struct pico_socket *sck);
|
||||
void pico_tcp_flags_update(struct pico_frame *f, struct pico_socket *s);
|
||||
int pico_tcp_set_bufsize_in(struct pico_socket *s, uint32_t value);
|
||||
int pico_tcp_set_bufsize_out(struct pico_socket *s, uint32_t value);
|
||||
int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value);
|
||||
int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value);
|
||||
int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value);
|
||||
int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value);
|
||||
int pico_tcp_set_keepalive_time(struct pico_socket *s, uint32_t value);
|
||||
int pico_tcp_set_linger(struct pico_socket *s, uint32_t value);
|
||||
uint16_t pico_tcp_get_socket_mss(struct pico_socket *s);
|
||||
int pico_tcp_check_listen_close(struct pico_socket *s);
|
||||
|
||||
#endif
|
||||
1323
kernel/picotcp/modules/pico_tftp.c
Normal file
1323
kernel/picotcp/modules/pico_tftp.c
Normal file
File diff suppressed because it is too large
Load Diff
83
kernel/picotcp/modules/pico_tftp.h
Normal file
83
kernel/picotcp/modules/pico_tftp.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
*********************************************************************/
|
||||
#ifndef PICO_TFTP_H
|
||||
#define PICO_TFTP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define PICO_TFTP_PORT (69)
|
||||
#define PICO_TFTP_PAYLOAD_SIZE (512)
|
||||
|
||||
#define PICO_TFTP_NONE 0
|
||||
#define PICO_TFTP_RRQ 1
|
||||
#define PICO_TFTP_WRQ 2
|
||||
#define PICO_TFTP_DATA 3
|
||||
#define PICO_TFTP_ACK 4
|
||||
#define PICO_TFTP_ERROR 5
|
||||
#define PICO_TFTP_OACK 6
|
||||
|
||||
/* Callback user events */
|
||||
#define PICO_TFTP_EV_OK 0
|
||||
#define PICO_TFTP_EV_OPT 1
|
||||
#define PICO_TFTP_EV_ERR_PEER 2
|
||||
#define PICO_TFTP_EV_ERR_LOCAL 3
|
||||
|
||||
/* TFTP ERROR CODES */
|
||||
#define TFTP_ERR_UNDEF 0
|
||||
#define TFTP_ERR_ENOENT 1
|
||||
#define TFTP_ERR_EACC 2
|
||||
#define TFTP_ERR_EXCEEDED 3
|
||||
#define TFTP_ERR_EILL 4
|
||||
#define TFTP_ERR_ETID 5
|
||||
#define TFTP_ERR_EEXIST 6
|
||||
#define TFTP_ERR_EUSR 7
|
||||
#define TFTP_ERR_EOPT 8
|
||||
|
||||
/* Session options */
|
||||
#define PICO_TFTP_OPTION_FILE 1
|
||||
|
||||
/* timeout: 0 -> adaptative, 1-255 -> fixed */
|
||||
#define PICO_TFTP_OPTION_TIME 2
|
||||
|
||||
|
||||
#define PICO_TFTP_MAX_TIMEOUT 255
|
||||
#define PICO_TFTP_MAX_FILESIZE (65535 * 512 - 1)
|
||||
|
||||
struct pico_tftp_session;
|
||||
|
||||
struct pico_tftp_session *pico_tftp_session_setup(union pico_address *a, uint16_t family);
|
||||
int pico_tftp_set_option(struct pico_tftp_session *session, uint8_t type, int32_t value);
|
||||
int pico_tftp_get_option(struct pico_tftp_session *session, uint8_t type, int32_t *value);
|
||||
|
||||
int pico_tftp_start_rx(struct pico_tftp_session *session, uint16_t port, const char *filename,
|
||||
int (*user_cb)(struct pico_tftp_session *session, uint16_t event, uint8_t *block, int32_t len, void *arg), void *arg);
|
||||
int pico_tftp_start_tx(struct pico_tftp_session *session, uint16_t port, const char *filename,
|
||||
int (*user_cb)(struct pico_tftp_session *session, uint16_t event, uint8_t *block, int32_t len, void *arg), void *arg);
|
||||
|
||||
int pico_tftp_reject_request(union pico_address *addr, uint16_t port, uint16_t error_code, const char *error_message);
|
||||
int32_t pico_tftp_send(struct pico_tftp_session *session, const uint8_t *data, int32_t len);
|
||||
|
||||
int pico_tftp_listen(uint16_t family, void (*cb)(union pico_address *addr, uint16_t port, uint16_t opcode, char *filename, int32_t len));
|
||||
|
||||
int pico_tftp_parse_request_args(char *args, int32_t len, int *options, uint8_t *timeout, int32_t *filesize);
|
||||
|
||||
int pico_tftp_abort(struct pico_tftp_session *session, uint16_t error, const char *reason);
|
||||
int pico_tftp_close_server(void);
|
||||
|
||||
int pico_tftp_get_file_size(struct pico_tftp_session *session, int32_t *file_size);
|
||||
|
||||
/* SPECIFIC APPLICATION DRIVEN FUNCTIONS */
|
||||
struct pico_tftp_session *pico_tftp_app_setup(union pico_address *a, uint16_t port, uint16_t family, int *synchro);
|
||||
|
||||
int pico_tftp_app_start_rx(struct pico_tftp_session *session, const char *filename);
|
||||
int pico_tftp_app_start_tx(struct pico_tftp_session *session, const char *filename);
|
||||
|
||||
int32_t pico_tftp_get(struct pico_tftp_session *session, uint8_t *data, int32_t len);
|
||||
int32_t pico_tftp_put(struct pico_tftp_session *session, uint8_t *data, int32_t len);
|
||||
|
||||
#endif
|
||||
222
kernel/picotcp/modules/pico_udp.c
Normal file
222
kernel/picotcp/modules/pico_udp.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_udp.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_eth.h"
|
||||
#include "pico_socket.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
#ifdef DEBUG_UDP
|
||||
#define udp_dbg dbg
|
||||
#else
|
||||
#define udp_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
|
||||
|
||||
/* Queues */
|
||||
static struct pico_queue udp_in = {
|
||||
0
|
||||
};
|
||||
static struct pico_queue udp_out = {
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
/* Functions */
|
||||
|
||||
uint16_t pico_udp_checksum_ipv4(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
|
||||
struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
|
||||
struct pico_socket *s = f->sock;
|
||||
struct pico_ipv4_pseudo_hdr pseudo;
|
||||
|
||||
if (s) {
|
||||
/* Case of outgoing frame */
|
||||
udp_dbg("UDP CRC: on outgoing frame\n");
|
||||
pseudo.src.addr = s->local_addr.ip4.addr;
|
||||
pseudo.dst.addr = s->remote_addr.ip4.addr;
|
||||
} else {
|
||||
/* Case of incomming frame */
|
||||
udp_dbg("UDP CRC: on incomming frame\n");
|
||||
pseudo.src.addr = hdr->src.addr;
|
||||
pseudo.dst.addr = hdr->dst.addr;
|
||||
}
|
||||
|
||||
pseudo.zeros = 0;
|
||||
pseudo.proto = PICO_PROTO_UDP;
|
||||
pseudo.len = short_be(f->transport_len);
|
||||
|
||||
return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), udp_hdr, f->transport_len);
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
uint16_t pico_udp_checksum_ipv6(struct pico_frame *f)
|
||||
{
|
||||
struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
|
||||
struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *)f->transport_hdr;
|
||||
struct pico_ipv6_pseudo_hdr pseudo = {
|
||||
.src = {{0}}, .dst = {{0}}, .len = 0, .zero = {0}, .nxthdr = 0
|
||||
};
|
||||
struct pico_socket *s = f->sock;
|
||||
struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *)f->info;
|
||||
|
||||
/* XXX If the IPv6 packet contains a Routing header, the Destination
|
||||
* Address used in the pseudo-header is that of the final destination */
|
||||
if (s) {
|
||||
/* Case of outgoing frame */
|
||||
pseudo.src = s->local_addr.ip6;
|
||||
if (remote_endpoint)
|
||||
pseudo.dst = remote_endpoint->remote_addr.ip6;
|
||||
else
|
||||
pseudo.dst = s->remote_addr.ip6;
|
||||
} else {
|
||||
/* Case of incomming frame */
|
||||
pseudo.src = ipv6_hdr->src;
|
||||
pseudo.dst = ipv6_hdr->dst;
|
||||
}
|
||||
|
||||
pseudo.len = long_be(f->transport_len);
|
||||
pseudo.nxthdr = PICO_PROTO_UDP;
|
||||
|
||||
return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv6_pseudo_hdr), udp_hdr, f->transport_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static int pico_udp_process_out(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(self);
|
||||
return (int)pico_network_send(f);
|
||||
}
|
||||
|
||||
static int pico_udp_push(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
struct pico_udp_hdr *hdr = (struct pico_udp_hdr *) f->transport_hdr;
|
||||
struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *) f->info;
|
||||
|
||||
/* this (fragmented) frame should contain a transport header */
|
||||
if (f->transport_hdr != f->payload) {
|
||||
hdr->trans.sport = f->sock->local_port;
|
||||
if (remote_endpoint) {
|
||||
hdr->trans.dport = remote_endpoint->remote_port;
|
||||
} else {
|
||||
hdr->trans.dport = f->sock->remote_port;
|
||||
}
|
||||
|
||||
hdr->len = short_be(f->transport_len);
|
||||
|
||||
/* do not perform CRC validation. If you want to, a system needs to be
|
||||
implemented to calculate the CRC over the total payload of a
|
||||
fragmented payload
|
||||
*/
|
||||
hdr->crc = 0;
|
||||
}
|
||||
|
||||
if (pico_enqueue(self->q_out, f) > 0) {
|
||||
return f->payload_len;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Interface: protocol definition */
|
||||
struct pico_protocol pico_proto_udp = {
|
||||
.name = "udp",
|
||||
.proto_number = PICO_PROTO_UDP,
|
||||
.layer = PICO_LAYER_TRANSPORT,
|
||||
.process_in = pico_transport_process_in,
|
||||
.process_out = pico_udp_process_out,
|
||||
.push = pico_udp_push,
|
||||
.q_in = &udp_in,
|
||||
.q_out = &udp_out,
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct pico_socket *pico_udp_open(void)
|
||||
{
|
||||
struct pico_socket_udp *u = PICO_ZALLOC(sizeof(struct pico_socket_udp));
|
||||
if (!u)
|
||||
return NULL;
|
||||
|
||||
u->mode = PICO_UDP_MODE_UNICAST;
|
||||
|
||||
#ifdef PICO_SUPPORT_MCAST
|
||||
u->mc_ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
|
||||
/* enable multicast loopback by default */
|
||||
u->sock.opt_flags |= (1 << PICO_SOCKET_OPT_MULTICAST_LOOP);
|
||||
#endif
|
||||
|
||||
return &u->sock;
|
||||
}
|
||||
|
||||
static void pico_udp_get_msginfo(struct pico_frame *f, struct pico_msginfo *msginfo)
|
||||
{
|
||||
if (!msginfo || !f->net_hdr)
|
||||
return;
|
||||
|
||||
msginfo->dev = f->dev;
|
||||
|
||||
if (IS_IPV4(f)) { /* IPV4 */
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)(f->net_hdr);
|
||||
msginfo->ttl = hdr->ttl;
|
||||
msginfo->tos = hdr->tos;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
|
||||
msginfo->ttl = hdr->hop;
|
||||
msginfo->tos = (hdr->vtf >> 20) & 0xFF; /* IPv6 traffic class */
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port, struct pico_msginfo *msginfo)
|
||||
{
|
||||
struct pico_frame *f = pico_queue_peek(&s->q_in);
|
||||
if (f) {
|
||||
if(!f->payload_len) {
|
||||
f->payload = f->transport_hdr + sizeof(struct pico_udp_hdr);
|
||||
f->payload_len = (uint16_t)(f->transport_len - sizeof(struct pico_udp_hdr));
|
||||
}
|
||||
|
||||
udp_dbg("expected: %d, got: %d\n", len, f->payload_len);
|
||||
if (src)
|
||||
pico_store_network_origin(src, f);
|
||||
|
||||
if (port) {
|
||||
struct pico_trans *hdr = (struct pico_trans *)f->transport_hdr;
|
||||
*port = hdr->sport;
|
||||
}
|
||||
|
||||
if (msginfo) {
|
||||
pico_udp_get_msginfo(f, msginfo);
|
||||
}
|
||||
|
||||
if (f->payload_len > len) {
|
||||
memcpy(buf, f->payload, len);
|
||||
f->payload += len;
|
||||
f->payload_len = (uint16_t)(f->payload_len - len);
|
||||
return len;
|
||||
} else {
|
||||
uint16_t ret = f->payload_len;
|
||||
memcpy(buf, f->payload, f->payload_len);
|
||||
f = pico_dequeue(&s->q_in);
|
||||
pico_frame_discard(f);
|
||||
return ret;
|
||||
}
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
45
kernel/picotcp/modules/pico_udp.h
Normal file
45
kernel/picotcp/modules/pico_udp.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
||||
|
||||
.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef INCLUDE_PICO_UDP
|
||||
#define INCLUDE_PICO_UDP
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_socket.h"
|
||||
#define PICO_UDP_MODE_UNICAST 0x01
|
||||
#define PICO_UDP_MODE_MULTICAST 0x02
|
||||
#define PICO_UDP_MODE_BROADCAST 0xFF
|
||||
|
||||
struct pico_socket_udp
|
||||
{
|
||||
struct pico_socket sock;
|
||||
int mode;
|
||||
uint8_t mc_ttl; /* Multicasting TTL */
|
||||
};
|
||||
|
||||
|
||||
extern struct pico_protocol pico_proto_udp;
|
||||
|
||||
PACKED_STRUCT_DEF pico_udp_hdr {
|
||||
struct pico_trans trans;
|
||||
uint16_t len;
|
||||
uint16_t crc;
|
||||
};
|
||||
#define PICO_UDPHDR_SIZE 8
|
||||
|
||||
struct pico_socket *pico_udp_open(void);
|
||||
uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port, struct pico_msginfo *msginfo);
|
||||
uint16_t pico_udp_checksum_ipv4(struct pico_frame *f);
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
uint16_t pico_udp_checksum_ipv6(struct pico_frame *f);
|
||||
#endif
|
||||
|
||||
|
||||
int pico_udp_setsockopt(struct pico_socket *s, int option, void *value);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user