Porting PicoTCP WIP

This commit is contained in:
2025-10-29 14:29:06 +01:00
parent 6722f42e68
commit 815c2239fe
464 changed files with 235009 additions and 24 deletions

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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

View 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

View 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);
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

View 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 */

View 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);
}
}

View 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

View 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;
}

View 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 */

View 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(&eth, 0, sizeof(struct ifreq));
strcpy(eth.ifr_name, name);
/* call the IOCTL */
if (ioctl(sck, SIOCGIFHWADDR, &eth) < 0) {
perror("ioctl(SIOCGIFHWADDR)");
return -1;
}
memcpy (mac, &eth.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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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;
}

View 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

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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 */

View 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

View 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 */

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

View 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

View 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 */

View 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 = &ethernet_in,
.q_out = &ethernet_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 */

View 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 */

View 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
}

View 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

View 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;
}

View 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 */

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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 */

View 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);
}

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

View 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

View 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 */

File diff suppressed because it is too large Load Diff

View 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

View 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 */

View 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

View 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 */

View 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 */

View 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 */

View 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
}

View 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

View 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
}

View 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

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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