dynamic loading of network stack no longer needed

This commit is contained in:
Joseph Henry
2017-04-06 19:16:01 -07:00
parent 997f12a592
commit 08cca3c7aa
463 changed files with 136513 additions and 0 deletions

View File

@@ -0,0 +1,674 @@
/*********************************************************************
PicoTCP. Copyright (c) 2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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
#define pico_aodv_dbg(...) do {} while(0)
/* #define pico_aodv_dbg dbg */
#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;
}
PICO_TREE_DECLARE(aodv_nodes, aodv_node_compare);
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));
pico_tree_insert(&aodv_nodes, node);
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: %llu now: %llu 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;
pico_timer_add(AODV_PATH_DISCOVERY_TIME, aodv_reverse_path_discover, origin);
}
}
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);
};
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);
pico_timer_add((pico_time)AODV_RING_TRAVERSAL_TIME(node->ring_ttl), aodv_retrans_rreq, node);
}
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++;
}
}
pico_timer_add((pico_time)AODV_RING_TRAVERSAL_TIME(1), aodv_retrans_rreq, node);
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);
}
}
pico_timer_add(AODV_HELLO_INTERVAL, pico_aodv_collector, NULL);
}
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();
pico_timer_add(AODV_HELLO_INTERVAL, pico_aodv_collector, NULL);
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 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,537 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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"
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_ethernet_send(f))
{
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++;
pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL);
}
void pico_arp_init(void)
{
pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL);
}
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);
}
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);
}
pico_frame_discard(f);
frames_queued[i] = NULL;
}
}
}
}
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]) {
frames_queued[i] = pico_frame_copy(f);
return;
}
}
/* Not possible to enqueue: caller will discard packet */
}
#ifdef DEBUG_ARP
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 */
pico_timer_add(PICO_ARP_TIMEOUT + stale->timestamp - now, arp_expire, stale);
}
}
static void pico_arp_add_entry(struct pico_arp *entry)
{
entry->arp_status = PICO_ARP_STATUS_REACHABLE;
entry->timestamp = PICO_TIME();
pico_tree_insert(&arp_tree, entry);
arp_dbg("ARP ## reachable.\n");
pico_arp_queued_trigger();
pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry);
}
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;
pico_arp_add_entry(arp);
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);
pico_arp_add_entry(found);
} 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)
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,66 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,307 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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;
}
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->mac);
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_mock_destroy((struct pico_device *)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);
pico_tree_insert(&mock_device_tree, mock);
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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(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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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(char *name);
#endif

View File

@@ -0,0 +1,96 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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 LICENSE and COPYING 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,220 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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 link_state = 0;
static void sig_handler(int signo)
{
if (signo == SIGUSR1)
link_state = 0;
if (signo == SIGUSR2)
link_state = 1;
}
static int tap_link_state(__attribute__((unused)) struct pico_device *self)
{
return 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;
}
if (tap_get_mac(name, mac) < 0) {
dbg("Tap mac query failed.\n");
pico_tap_destroy((struct pico_device *)tap);
return NULL;
}
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,115 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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
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 *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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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

View File

@@ -0,0 +1,990 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING for usage.
Authors: Kristof Roelants, Frederik Van Slycken
*********************************************************************/
#include "pico_dhcp_client.h"
#include "pico_stack.h"
#include "pico_config.h"
#include "pico_device.h"
#include "pico_ipv4.h"
#include "pico_socket.h"
#include "pico_eth.h"
#if (defined PICO_SUPPORT_DHCPC && defined PICO_SUPPORT_UDP)
#define dhcpc_dbg(...) do {} while(0)
/* #define dhcpc_dbg dbg */
/* timer values */
#define DHCP_CLIENT_REINIT 6000 /* msec */
#define DHCP_CLIENT_RETRANS 4 /* sec */
#define DHCP_CLIENT_RETRIES 3
#define DHCP_CLIENT_TIMER_STOPPED 0
#define DHCP_CLIENT_TIMER_STARTED 1
/* maximum size of a DHCP message */
#define DHCP_CLIENT_MAXMSGZISE (PICO_IP_MRU - PICO_SIZE_IP4HDR)
#define PICO_DHCP_HOSTNAME_MAXLEN 64U
static char dhcpc_host_name[PICO_DHCP_HOSTNAME_MAXLEN] = "";
static char dhcpc_domain_name[PICO_DHCP_HOSTNAME_MAXLEN] = "";
enum dhcp_client_state {
DHCP_CLIENT_STATE_INIT_REBOOT = 0,
DHCP_CLIENT_STATE_REBOOTING,
DHCP_CLIENT_STATE_INIT,
DHCP_CLIENT_STATE_SELECTING,
DHCP_CLIENT_STATE_REQUESTING,
DHCP_CLIENT_STATE_BOUND,
DHCP_CLIENT_STATE_RENEWING,
DHCP_CLIENT_STATE_REBINDING
};
#define PICO_DHCPC_TIMER_INIT 0
#define PICO_DHCPC_TIMER_REQUEST 1
#define PICO_DHCPC_TIMER_RENEW 2
#define PICO_DHCPC_TIMER_REBIND 3
#define PICO_DHCPC_TIMER_T1 4
#define PICO_DHCPC_TIMER_T2 5
#define PICO_DHCPC_TIMER_LEASE 6
#define PICO_DHCPC_TIMER_ARRAY_SIZE 7
struct dhcp_client_timer
{
uint8_t state;
unsigned int type;
uint32_t xid;
};
struct pico_dhcp_client_cookie
{
uint8_t event;
uint8_t retry;
uint32_t xid;
uint32_t *uid;
enum dhcp_client_state state;
void (*cb)(void*dhcpc, int code);
pico_time init_timestamp;
struct pico_socket *s;
struct pico_ip4 address;
struct pico_ip4 netmask;
struct pico_ip4 gateway;
struct pico_ip4 nameserver[2];
struct pico_ip4 server_id;
struct pico_device *dev;
struct dhcp_client_timer *timer[PICO_DHCPC_TIMER_ARRAY_SIZE];
uint32_t t1_time;
uint32_t t2_time;
uint32_t lease_time;
uint32_t renew_time;
uint32_t rebind_time;
};
static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc);
static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type);
static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s);
static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
static const struct pico_ip4 bcast_netmask = {
.addr = 0xFFFFFFFF
};
static struct pico_ip4 inaddr_any = {
0
};
static int dhcp_cookies_cmp(void *ka, void *kb)
{
struct pico_dhcp_client_cookie *a = ka, *b = kb;
if (a->xid == b->xid)
return 0;
return (a->xid < b->xid) ? (-1) : (1);
}
PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp);
static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid, struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
{
struct pico_dhcp_client_cookie *dhcpc = NULL, *found = NULL, test = {
0
};
test.xid = xid;
found = pico_tree_findKey(&DHCPCookies, &test);
if (found) {
pico_err = PICO_ERR_EAGAIN;
return NULL;
}
dhcpc = PICO_ZALLOC(sizeof(struct pico_dhcp_client_cookie));
if (!dhcpc) {
pico_err = PICO_ERR_ENOMEM;
return NULL;
}
dhcpc->state = DHCP_CLIENT_STATE_INIT;
dhcpc->xid = xid;
dhcpc->uid = uid;
*(dhcpc->uid) = 0;
dhcpc->cb = cb;
dhcpc->dev = dev;
pico_tree_insert(&DHCPCookies, dhcpc);
return dhcpc;
}
static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc);
static int pico_dhcp_client_del_cookie(uint32_t xid)
{
struct pico_dhcp_client_cookie test = {
0
}, *found = NULL;
test.xid = xid;
found = pico_tree_findKey(&DHCPCookies, &test);
if (!found)
return -1;
pico_dhcp_client_stop_timers(found);
pico_socket_close(found->s);
found->s = NULL;
pico_ipv4_link_del(found->dev, found->address);
pico_tree_delete(&DHCPCookies, found);
PICO_FREE(found);
return 0;
}
static struct pico_dhcp_client_cookie *pico_dhcp_client_find_cookie(uint32_t xid)
{
struct pico_dhcp_client_cookie test = {
0
}, *found = NULL;
test.xid = xid;
found = pico_tree_findKey(&DHCPCookies, &test);
if (found)
return found;
else
return NULL;
}
static void pico_dhcp_client_timer_handler(pico_time now, void *arg);
static void pico_dhcp_client_reinit(pico_time now, void *arg);
static struct dhcp_client_timer *pico_dhcp_timer_add(uint8_t type, uint32_t time, struct pico_dhcp_client_cookie *ck)
{
struct dhcp_client_timer *t;
t = PICO_ZALLOC(sizeof(struct dhcp_client_timer));
if (!t)
return NULL;
t->state = DHCP_CLIENT_TIMER_STARTED;
t->xid = ck->xid;
t->type = type;
pico_timer_add(time, pico_dhcp_client_timer_handler, t);
if (ck->timer[type]) {
ck->timer[type]->state = DHCP_CLIENT_TIMER_STOPPED;
}
ck->timer[type] = t;
return t;
}
static int dhcp_get_timer_event(struct pico_dhcp_client_cookie *dhcpc, unsigned int type)
{
const int events[PICO_DHCPC_TIMER_ARRAY_SIZE] =
{
PICO_DHCP_EVENT_RETRANSMIT,
PICO_DHCP_EVENT_RETRANSMIT,
PICO_DHCP_EVENT_RETRANSMIT,
PICO_DHCP_EVENT_RETRANSMIT,
PICO_DHCP_EVENT_T1,
PICO_DHCP_EVENT_T2,
PICO_DHCP_EVENT_LEASE
};
if (type == PICO_DHCPC_TIMER_REQUEST) {
if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
reset(dhcpc, NULL);
return PICO_DHCP_EVENT_NONE;
}
} else if (type < PICO_DHCPC_TIMER_T1) {
dhcpc->retry++;
}
return events[type];
}
static void pico_dhcp_client_timer_handler(pico_time now, void *arg)
{
struct dhcp_client_timer *t = (struct dhcp_client_timer *)arg;
struct pico_dhcp_client_cookie *dhcpc;
if (!t)
return;
(void) now;
if (t->state != DHCP_CLIENT_TIMER_STOPPED) {
dhcpc = pico_dhcp_client_find_cookie(t->xid);
if (dhcpc && dhcpc->timer) {
t->state = DHCP_CLIENT_TIMER_STOPPED;
if ((t->type == PICO_DHCPC_TIMER_INIT) && (dhcpc->state < DHCP_CLIENT_STATE_SELECTING)) {
pico_dhcp_client_reinit(now, dhcpc);
} else if (t->type != PICO_DHCPC_TIMER_INIT) {
dhcpc->event = (uint8_t)dhcp_get_timer_event(dhcpc, t->type);
if (dhcpc->event != PICO_DHCP_EVENT_NONE)
pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
}
}
}
}
static void pico_dhcp_client_timer_stop(struct pico_dhcp_client_cookie *dhcpc, int type)
{
if (dhcpc->timer[type]) {
dhcpc->timer[type]->state = DHCP_CLIENT_TIMER_STOPPED;
}
}
static void pico_dhcp_client_reinit(pico_time now, void *arg)
{
struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
(void) now;
if (dhcpc->s) {
pico_socket_close(dhcpc->s);
dhcpc->s = NULL;
}
if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
pico_err = PICO_ERR_EAGAIN;
if (dhcpc->cb)
dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
pico_dhcp_client_del_cookie(dhcpc->xid);
return;
}
pico_dhcp_client_init(dhcpc);
return;
}
static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc)
{
int i;
dhcpc->retry = 0;
for (i = 0; i < PICO_DHCPC_TIMER_ARRAY_SIZE; i++)
pico_dhcp_client_timer_stop(dhcpc, i);
}
static void pico_dhcp_client_start_init_timer(struct pico_dhcp_client_cookie *dhcpc)
{
uint32_t time = 0;
/* timer value is doubled with every retry (exponential backoff) */
time = (uint32_t) (DHCP_CLIENT_RETRANS << dhcpc->retry);
pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, time * 1000, dhcpc);
}
static void pico_dhcp_client_start_requesting_timer(struct pico_dhcp_client_cookie *dhcpc)
{
uint32_t time = 0;
/* timer value is doubled with every retry (exponential backoff) */
time = (uint32_t)(DHCP_CLIENT_RETRANS << dhcpc->retry);
pico_dhcp_timer_add(PICO_DHCPC_TIMER_REQUEST, time * 1000, dhcpc);
}
static void pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie *dhcpc)
{
uint32_t halftime = 0;
/* wait one-half of the remaining time until T2, down to a minimum of 60 seconds */
/* (dhcpc->retry + 1): initial -> divide by 2, 1st retry -> divide by 4, 2nd retry -> divide by 8, etc */
pico_dhcp_client_stop_timers(dhcpc);
halftime = dhcpc->renew_time >> (dhcpc->retry + 1);
if (halftime < 60)
halftime = 60;
pico_dhcp_timer_add(PICO_DHCPC_TIMER_RENEW, halftime * 1000, dhcpc);
return;
}
static void pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cookie *dhcpc)
{
uint32_t halftime = 0;
pico_dhcp_client_stop_timers(dhcpc);
halftime = dhcpc->rebind_time >> (dhcpc->retry + 1);
if (halftime < 60)
halftime = 60;
pico_dhcp_timer_add(PICO_DHCPC_TIMER_REBIND, halftime * 1000, dhcpc);
return;
}
static void pico_dhcp_client_start_reacquisition_timers(struct pico_dhcp_client_cookie *dhcpc)
{
pico_dhcp_client_stop_timers(dhcpc);
pico_dhcp_timer_add(PICO_DHCPC_TIMER_T1, dhcpc->t1_time * 1000, dhcpc);
pico_dhcp_timer_add(PICO_DHCPC_TIMER_T2, dhcpc->t2_time * 1000, dhcpc);
pico_dhcp_timer_add(PICO_DHCPC_TIMER_LEASE, dhcpc->lease_time * 1000, dhcpc);
}
static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc)
{
uint16_t port = PICO_DHCP_CLIENT_PORT;
if (!dhcpc)
return -1;
/* adding a link with address 0.0.0.0 and netmask 0.0.0.0,
* automatically adds a route for a global broadcast */
pico_ipv4_link_add(dhcpc->dev, inaddr_any, bcast_netmask);
if (!dhcpc->s)
dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup);
if (!dhcpc->s) {
pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc);
return 0;
}
dhcpc->s->dev = dhcpc->dev;
if (pico_socket_bind(dhcpc->s, &inaddr_any, &port) < 0) {
pico_socket_close(dhcpc->s);
dhcpc->s = NULL;
pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc);
return 0;
}
if (pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER) < 0) {
pico_socket_close(dhcpc->s);
dhcpc->s = NULL;
pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc);
return 0;
}
dhcpc->retry = 0;
dhcpc->init_timestamp = PICO_TIME_MS();
pico_dhcp_client_start_init_timer(dhcpc);
return 0;
}
int pico_dhcp_initiate_negotiation(struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
{
uint8_t retry = 32;
uint32_t xid = 0;
struct pico_dhcp_client_cookie *dhcpc = NULL;
if (!dev || !cb || !uid) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
if (!dev->eth) {
pico_err = PICO_ERR_EOPNOTSUPP;
return -1;
}
/* attempt to generate a correct xid, else fail */
do {
xid = pico_rand();
} while (!xid && --retry);
if (!xid) {
pico_err = PICO_ERR_EAGAIN;
return -1;
}
dhcpc = pico_dhcp_client_add_cookie(xid, dev, cb, uid);
if (!dhcpc)
return -1;
dhcpc_dbg("DHCP client: cookie with xid %u\n", dhcpc->xid);
*uid = xid;
return pico_dhcp_client_init(dhcpc);
}
static void pico_dhcp_client_recv_params(struct pico_dhcp_client_cookie *dhcpc, struct pico_dhcp_opt *opt)
{
do {
switch (opt->code)
{
case PICO_DHCP_OPT_PAD:
break;
case PICO_DHCP_OPT_END:
break;
case PICO_DHCP_OPT_MSGTYPE:
dhcpc->event = opt->ext.msg_type.type;
dhcpc_dbg("DHCP client: message type %u\n", dhcpc->event);
break;
case PICO_DHCP_OPT_LEASETIME:
dhcpc->lease_time = long_be(opt->ext.lease_time.time);
dhcpc_dbg("DHCP client: lease time %u\n", dhcpc->lease_time);
break;
case PICO_DHCP_OPT_RENEWALTIME:
dhcpc->t1_time = long_be(opt->ext.renewal_time.time);
dhcpc_dbg("DHCP client: renewal time %u\n", dhcpc->t1_time);
break;
case PICO_DHCP_OPT_REBINDINGTIME:
dhcpc->t2_time = long_be(opt->ext.rebinding_time.time);
dhcpc_dbg("DHCP client: rebinding time %u\n", dhcpc->t2_time);
break;
case PICO_DHCP_OPT_ROUTER:
dhcpc->gateway = opt->ext.router.ip;
dhcpc_dbg("DHCP client: router %08X\n", dhcpc->gateway.addr);
break;
case PICO_DHCP_OPT_DNS:
dhcpc->nameserver[0] = opt->ext.dns1.ip;
dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[0].addr);
if (opt->len >= 8) {
dhcpc->nameserver[1] = opt->ext.dns2.ip;
dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[1].addr);
}
break;
case PICO_DHCP_OPT_NETMASK:
dhcpc->netmask = opt->ext.netmask.ip;
dhcpc_dbg("DHCP client: netmask %08X\n", dhcpc->netmask.addr);
break;
case PICO_DHCP_OPT_SERVERID:
dhcpc->server_id = opt->ext.server_id.ip;
dhcpc_dbg("DHCP client: server ID %08X\n", dhcpc->server_id.addr);
break;
case PICO_DHCP_OPT_OPTOVERLOAD:
dhcpc_dbg("DHCP client: WARNING option overload present (not processed)");
break;
case PICO_DHCP_OPT_HOSTNAME:
{
uint32_t maxlen = PICO_DHCP_HOSTNAME_MAXLEN;
if (opt->len < maxlen)
maxlen = opt->len;
strncpy(dhcpc_host_name, opt->ext.string.txt, maxlen);
}
break;
case PICO_DHCP_OPT_DOMAINNAME:
{
uint32_t maxlen = PICO_DHCP_HOSTNAME_MAXLEN;
if (opt->len < maxlen)
maxlen = opt->len;
strncpy(dhcpc_domain_name, opt->ext.string.txt, maxlen);
}
break;
default:
dhcpc_dbg("DHCP client: WARNING unsupported option %u\n", opt->code);
break;
}
} while (pico_dhcp_next_option(&opt));
/* default values for T1 and T2 when not provided */
if (!dhcpc->t1_time)
dhcpc->t1_time = dhcpc->lease_time >> 1;
if (!dhcpc->t2_time)
dhcpc->t2_time = (dhcpc->lease_time * 875) / 1000;
return;
}
static int recv_offer(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
struct pico_dhcp_opt *opt = DHCP_OPT(hdr,0);
pico_dhcp_client_recv_params(dhcpc, opt);
if ((dhcpc->event != PICO_DHCP_MSG_OFFER) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time)
return -1;
dhcpc->address.addr = hdr->yiaddr;
/* we skip state SELECTING, process first offer received */
dhcpc->state = DHCP_CLIENT_STATE_REQUESTING;
dhcpc->retry = 0;
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
pico_dhcp_client_start_requesting_timer(dhcpc);
return 0;
}
static int recv_ack(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
struct pico_dhcp_opt *opt = DHCP_OPT(hdr,0);
struct pico_ip4 address = {
0
};
struct pico_ip4 any_address = {
0
};
struct pico_ipv4_link *l;
pico_dhcp_client_recv_params(dhcpc, opt);
if ((dhcpc->event != PICO_DHCP_MSG_ACK) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time)
return -1;
/* Issue #20 the server can transmit on ACK a different IP than the one in OFFER */
/* RFC2131 ch 4.3.2 ... The client SHOULD use the parameters in the DHCPACK message for configuration */
if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING)
dhcpc->address.addr = hdr->yiaddr;
/* close the socket used for address (re)acquisition */
pico_socket_close(dhcpc->s);
dhcpc->s = NULL;
/* Delete all the links before adding the address */
pico_ipv4_link_del(dhcpc->dev, address);
l = pico_ipv4_link_by_dev(dhcpc->dev);
while(l) {
pico_ipv4_link_del(dhcpc->dev, l->address);
l = pico_ipv4_link_by_dev_next(dhcpc->dev, l);
}
pico_ipv4_link_add(dhcpc->dev, dhcpc->address, dhcpc->netmask);
dbg("DHCP client: renewal time (T1) %u\n", (unsigned int)dhcpc->t1_time);
dbg("DHCP client: rebinding time (T2) %u\n", (unsigned int)dhcpc->t2_time);
dbg("DHCP client: lease time %u\n", (unsigned int)dhcpc->lease_time);
/* If router option is received, use it as default gateway */
if (dhcpc->gateway.addr != 0U) {
pico_ipv4_route_add(any_address, any_address, dhcpc->gateway, 1, NULL);
}
dhcpc->retry = 0;
dhcpc->renew_time = dhcpc->t2_time - dhcpc->t1_time;
dhcpc->rebind_time = dhcpc->lease_time - dhcpc->t2_time;
pico_dhcp_client_start_reacquisition_timers(dhcpc);
*(dhcpc->uid) = dhcpc->xid;
if (dhcpc->cb)
dhcpc->cb(dhcpc, PICO_DHCP_SUCCESS);
dhcpc->state = DHCP_CLIENT_STATE_BOUND;
return 0;
}
static int renew(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
uint16_t port = PICO_DHCP_CLIENT_PORT;
(void) buf;
dhcpc->state = DHCP_CLIENT_STATE_RENEWING;
dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup);
if (!dhcpc->s) {
dhcpc_dbg("DHCP client ERROR: failure opening socket on renew, aborting DHCP! (%s)\n", strerror(pico_err));
if (dhcpc->cb)
dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
return -1;
}
if (pico_socket_bind(dhcpc->s, &dhcpc->address, &port) != 0) {
dhcpc_dbg("DHCP client ERROR: failure binding socket on renew, aborting DHCP! (%s)\n", strerror(pico_err));
pico_socket_close(dhcpc->s);
dhcpc->s = NULL;
if (dhcpc->cb)
dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
return -1;
}
dhcpc->retry = 0;
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
pico_dhcp_client_start_renewing_timer(dhcpc);
return 0;
}
static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
(void) buf;
dhcpc->state = DHCP_CLIENT_STATE_REBINDING;
dhcpc->retry = 0;
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
pico_dhcp_client_start_rebinding_timer(dhcpc);
return 0;
}
static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
struct pico_ip4 address = {
0
};
(void) buf;
if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING)
address.addr = PICO_IP4_ANY;
else
address.addr = dhcpc->address.addr;
/* close the socket used for address (re)acquisition */
pico_socket_close(dhcpc->s);
dhcpc->s = NULL;
/* delete the link with the currently in use address */
pico_ipv4_link_del(dhcpc->dev, address);
if (dhcpc->cb)
dhcpc->cb(dhcpc, PICO_DHCP_RESET);
if (dhcpc->state < DHCP_CLIENT_STATE_BOUND)
{
/* pico_dhcp_client_timer_stop(dhcpc, PICO_DHCPC_TIMER_INIT); */
}
dhcpc->state = DHCP_CLIENT_STATE_INIT;
pico_dhcp_client_stop_timers(dhcpc);
pico_dhcp_client_init(dhcpc);
return 0;
}
static int retransmit(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
(void) buf;
switch (dhcpc->state)
{
case DHCP_CLIENT_STATE_INIT:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
pico_dhcp_client_start_init_timer(dhcpc);
break;
case DHCP_CLIENT_STATE_REQUESTING:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
pico_dhcp_client_start_requesting_timer(dhcpc);
break;
case DHCP_CLIENT_STATE_RENEWING:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
pico_dhcp_client_start_renewing_timer(dhcpc);
break;
case DHCP_CLIENT_STATE_REBINDING:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
pico_dhcp_client_start_rebinding_timer(dhcpc);
break;
default:
dhcpc_dbg("DHCP client WARNING: retransmit in incorrect state (%u)!\n", dhcpc->state);
return -1;
}
return 0;
}
struct dhcp_action_entry {
int (*offer)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
int (*ack)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
int (*nak)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
int (*timer1)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
int (*timer2)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
int (*timer_lease)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
int (*timer_retransmit)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
};
static struct dhcp_action_entry dhcp_fsm[] =
{ /* event |offer |ack |nak |T1 |T2 |lease |retransmit */
/* state init-reboot */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL },
/* state rebooting */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL },
/* state init */ { recv_offer, NULL, NULL, NULL, NULL, NULL, retransmit },
/* state selecting */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL },
/* state requesting */ { NULL, recv_ack, reset, NULL, NULL, NULL, retransmit },
/* state bound */ { NULL, NULL, NULL, renew, NULL, NULL, NULL },
/* state renewing */ { NULL, recv_ack, reset, NULL, rebind, NULL, retransmit },
/* state rebinding */ { NULL, recv_ack, reset, NULL, NULL, reset, retransmit },
};
/* TIMERS REMARK:
* In state bound we have T1, T2 and the lease timer running. If T1 goes off, we attempt to renew.
* If the renew succeeds a new T1, T2 and lease timer is started. The former T2 and lease timer is
* still running though. This poses no concerns as the T2 and lease event in state bound have a NULL
* pointer in the fsm. If the former T2 or lease timer goes off, nothing happens. Same situation
* applies for T2 and a succesfull rebind. */
static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
switch (event)
{
case PICO_DHCP_MSG_OFFER:
dhcpc_dbg("DHCP client: received OFFER\n");
if (dhcp_fsm[dhcpc->state].offer)
dhcp_fsm[dhcpc->state].offer(dhcpc, buf);
break;
case PICO_DHCP_MSG_ACK:
dhcpc_dbg("DHCP client: received ACK\n");
if (dhcp_fsm[dhcpc->state].ack)
dhcp_fsm[dhcpc->state].ack(dhcpc, buf);
break;
case PICO_DHCP_MSG_NAK:
dhcpc_dbg("DHCP client: received NAK\n");
if (dhcp_fsm[dhcpc->state].nak)
dhcp_fsm[dhcpc->state].nak(dhcpc, buf);
break;
case PICO_DHCP_EVENT_T1:
dhcpc_dbg("DHCP client: received T1 timeout\n");
if (dhcp_fsm[dhcpc->state].timer1)
dhcp_fsm[dhcpc->state].timer1(dhcpc, NULL);
break;
case PICO_DHCP_EVENT_T2:
dhcpc_dbg("DHCP client: received T2 timeout\n");
if (dhcp_fsm[dhcpc->state].timer2)
dhcp_fsm[dhcpc->state].timer2(dhcpc, NULL);
break;
case PICO_DHCP_EVENT_LEASE:
dhcpc_dbg("DHCP client: received LEASE timeout\n");
if (dhcp_fsm[dhcpc->state].timer_lease)
dhcp_fsm[dhcpc->state].timer_lease(dhcpc, NULL);
break;
case PICO_DHCP_EVENT_RETRANSMIT:
dhcpc_dbg("DHCP client: received RETRANSMIT timeout\n");
if (dhcp_fsm[dhcpc->state].timer_retransmit)
dhcp_fsm[dhcpc->state].timer_retransmit(dhcpc, NULL);
break;
default:
dhcpc_dbg("DHCP client WARNING: unrecognized event (%u)!\n", dhcpc->event);
return;
}
return;
}
static int16_t pico_dhcp_client_opt_parse(void *ptr, uint16_t len)
{
uint32_t optlen = len - (uint32_t)sizeof(struct pico_dhcp_hdr);
struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)ptr;
struct pico_dhcp_opt *opt = DHCP_OPT(hdr,0);
if (hdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE)
return -1;
if (!pico_dhcp_are_options_valid(opt, (int32_t)optlen))
return -1;
do {
if (opt->code == PICO_DHCP_OPT_MSGTYPE)
return opt->ext.msg_type.type;
} while (pico_dhcp_next_option(&opt));
return -1;
}
static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type)
{
int32_t r = 0;
uint16_t optlen = 0, offset = 0;
struct pico_ip4 destination = {
.addr = 0xFFFFFFFF
};
struct pico_dhcp_hdr *hdr = NULL;
/* RFC 2131 3.1.3: Request is always BROADCAST */
/* Set again default route for the bcast request */
pico_ipv4_route_set_bcast_link(pico_ipv4_link_by_dev(dhcpc->dev));
switch (msg_type)
{
case PICO_DHCP_MSG_DISCOVER:
dhcpc_dbg("DHCP client: sent DHCPDISCOVER\n");
optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_END;
hdr = PICO_ZALLOC((size_t)(sizeof(struct pico_dhcp_hdr) + optlen));
if (!hdr) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
/* specific options */
offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(DHCP_OPT(hdr,offset), DHCP_CLIENT_MAXMSGZISE));
break;
case PICO_DHCP_MSG_REQUEST:
optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_REQIP + PICO_DHCP_OPTLEN_SERVERID
+ PICO_DHCP_OPTLEN_END;
hdr = PICO_ZALLOC(sizeof(struct pico_dhcp_hdr) + optlen);
if (!hdr) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
/* specific options */
offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(DHCP_OPT(hdr,offset), DHCP_CLIENT_MAXMSGZISE));
if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) {
offset = (uint16_t)(offset + pico_dhcp_opt_reqip(DHCP_OPT(hdr,offset), &dhcpc->address));
offset = (uint16_t)(offset + pico_dhcp_opt_serverid(DHCP_OPT(hdr,offset), &dhcpc->server_id));
}
break;
default:
return -1;
}
/* common options */
offset = (uint16_t)(offset + pico_dhcp_opt_msgtype(DHCP_OPT(hdr,offset), msg_type));
offset = (uint16_t)(offset + pico_dhcp_opt_paramlist(DHCP_OPT(hdr,offset)));
offset = (uint16_t)(offset + pico_dhcp_opt_end(DHCP_OPT(hdr,offset)));
switch (dhcpc->state)
{
case DHCP_CLIENT_STATE_BOUND:
destination.addr = dhcpc->server_id.addr;
hdr->ciaddr = dhcpc->address.addr;
break;
case DHCP_CLIENT_STATE_RENEWING:
destination.addr = dhcpc->server_id.addr;
hdr->ciaddr = dhcpc->address.addr;
break;
case DHCP_CLIENT_STATE_REBINDING:
hdr->ciaddr = dhcpc->address.addr;
break;
default:
/* do nothing */
break;
}
/* header information */
hdr->op = PICO_DHCP_OP_REQUEST;
hdr->htype = PICO_DHCP_HTYPE_ETH;
hdr->hlen = PICO_SIZE_ETH;
hdr->xid = dhcpc->xid;
/* hdr->flags = short_be(PICO_DHCP_FLAG_BROADCAST); / * Nope: see bug #96! * / */
hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
/* copy client hardware address */
memcpy(hdr->hwaddr, &dhcpc->dev->eth->mac, PICO_SIZE_ETH);
if (destination.addr == PICO_IP4_BCAST)
pico_ipv4_route_set_bcast_link(pico_ipv4_link_get(&dhcpc->address));
r = pico_socket_sendto(dhcpc->s, hdr, (int)(sizeof(struct pico_dhcp_hdr) + optlen), &destination, PICO_DHCPD_PORT);
PICO_FREE(hdr);
if (r < 0)
return -1;
return 0;
}
static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s)
{
uint8_t *buf;
int r = 0;
struct pico_dhcp_hdr *hdr = NULL;
struct pico_dhcp_client_cookie *dhcpc = NULL;
if ((ev & PICO_SOCK_EV_RD) == 0)
return;
buf = PICO_ZALLOC(DHCP_CLIENT_MAXMSGZISE);
if (!buf) {
return;
}
r = pico_socket_recvfrom(s, buf, DHCP_CLIENT_MAXMSGZISE, NULL, NULL);
if (r < 0)
goto out_discard_buf;
/* If the 'xid' of an arriving message does not match the 'xid'
* of the most recent transmitted message, the message must be
* silently discarded. */
hdr = (struct pico_dhcp_hdr *)buf;
dhcpc = pico_dhcp_client_find_cookie(hdr->xid);
if (!dhcpc)
goto out_discard_buf;
dhcpc->event = (uint8_t)pico_dhcp_client_opt_parse(buf, (uint16_t)r);
pico_dhcp_state_machine(dhcpc->event, dhcpc, buf);
out_discard_buf:
PICO_FREE(buf);
}
void *pico_dhcp_get_identifier(uint32_t xid)
{
return (void *)pico_dhcp_client_find_cookie(xid);
}
struct pico_ip4 pico_dhcp_get_address(void*dhcpc)
{
return ((struct pico_dhcp_client_cookie*)dhcpc)->address;
}
struct pico_ip4 pico_dhcp_get_gateway(void*dhcpc)
{
return ((struct pico_dhcp_client_cookie*)dhcpc)->gateway;
}
struct pico_ip4 pico_dhcp_get_netmask(void *dhcpc)
{
return ((struct pico_dhcp_client_cookie*)dhcpc)->netmask;
}
struct pico_ip4 pico_dhcp_get_nameserver(void*dhcpc, int index)
{
struct pico_ip4 fault = {
.addr = 0xFFFFFFFFU
};
if ((index != 0) && (index != 1))
return fault;
return ((struct pico_dhcp_client_cookie*)dhcpc)->nameserver[index];
}
int pico_dhcp_client_abort(uint32_t xid)
{
return pico_dhcp_client_del_cookie(xid);
}
char *pico_dhcp_get_hostname(void)
{
return dhcpc_host_name;
}
char *pico_dhcp_get_domain(void)
{
return dhcpc_domain_name;
}
#endif

View File

@@ -0,0 +1,32 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,411 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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)
#define dhcps_dbg(...) do {} while(0)
/* #define dhcps_dbg dbg */
/* 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);
}
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);
}
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;
}
pico_tree_insert(&DHCPSettings, dhcps);
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);
pico_tree_insert(&DHCPNegotiations, dhcpn);
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,808 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING 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
#define dns_dbg(...) do {} while(0)
/* #define dns_dbg dbg */
/* 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);
}
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);
}
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 */
PICO_FREE(dns);
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) {
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", short_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;
pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, paramID);
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_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);
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,46 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING 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
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,523 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING 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
#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, unsigned int 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, unsigned int 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
* @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,549 @@
/*********************************************************************
PicoTCP. Copyright (c) 2014-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING for usage.
.
Author: Jelle De Vleeschouwer
*********************************************************************/
#include "pico_dns_sd.h"
#ifdef PICO_SUPPORT_DNS_SD
/* --- Debugging --- */
#define dns_sd_dbg(...) do {} while(0)
//#define dns_sd_dbg dbg
/* --- 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 it's 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)
pico_tree_insert(&rtree, txt_record);
pico_tree_insert(&rtree, srv_record);
if (pico_mdns_claim(rtree, callback, arg)) {
PICO_MDNS_RTREE_DESTROY(&rtree);
return -1;
}
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 LICENSE and COPYING 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,391 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING 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"
#define frag_dbg(...) do {} while(0)
#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)((x->ext.frag.id[0] << 24) + (x->ext.frag.id[1] << 16) + \
(x->ext.frag.id[2] << 8) + 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 FRAG_OFF(net, frag) ((net == PICO_PROTO_IPV4) ? (IP4_FRAG_OFF(frag)) : (IP6_FRAG_OFF(frag)))
#define FRAG_MORE(net, frag) ((net == PICO_PROTO_IPV4) ? (IP4_FRAG_MORE(frag)) : (IP6_FRAG_MORE(frag)))
#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(uint8_t proto, uint8_t net);
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
static uint32_t ipv6_cur_frag_id = 0u;
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;
}
PICO_TREE_DECLARE(ipv6_fragments, pico_ipv6_frag_compare);
static void pico_ipv6_fragments_complete(unsigned int len, uint8_t proto)
{
struct pico_tree_node *index, *tmp;
struct pico_frame *f;
unsigned int bookmark = 0;
struct pico_frame *full = NULL;
struct pico_frame *first = pico_tree_first(&ipv6_fragments);
full = pico_frame_alloc((uint16_t)(PICO_SIZE_IP6HDR + len));
if (full) {
full->net_hdr = full->buffer;
full->net_len = PICO_SIZE_IP6HDR;
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, &ipv6_fragments, tmp) {
f = index->keyValue;
memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len);
bookmark += f->transport_len;
pico_tree_delete(&ipv6_fragments, f);
pico_frame_discard(f);
}
if (pico_transport_receive(full, proto) == -1)
{
pico_frame_discard(full);
}
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);
}
static int pico_ipv6_frag_match(struct pico_frame *a, struct pico_frame *b)
{
struct pico_ipv6_hdr *ha, *hb;
if (!a || !b)
return 0;
ha = (struct pico_ipv6_hdr *)a->net_hdr;
hb = (struct pico_ipv6_hdr *)b->net_hdr;
if (!ha || !hb)
return 0;
if (memcmp(ha->src.addr, hb->src.addr, PICO_SIZE_IP6) != 0)
return 0;
if (memcmp(ha->dst.addr, hb->dst.addr, PICO_SIZE_IP6) != 0)
return 0;
return 1;
}
#endif
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
static uint32_t ipv4_cur_frag_id = 0u;
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;
}
PICO_TREE_DECLARE(ipv4_fragments, pico_ipv4_frag_compare);
static void pico_ipv4_fragments_complete(unsigned int len, uint8_t proto)
{
struct pico_tree_node *index, *tmp;
struct pico_frame *f;
unsigned int bookmark = 0;
struct pico_frame *full = NULL;
struct pico_frame *first = pico_tree_first(&ipv4_fragments);
full = pico_frame_alloc((uint16_t)(PICO_SIZE_IP4HDR + len));
if (full) {
full->net_hdr = full->buffer;
full->net_len = PICO_SIZE_IP4HDR;
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, &ipv4_fragments, tmp) {
f = index->keyValue;
memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len);
bookmark += f->transport_len;
pico_tree_delete(&ipv4_fragments, f);
pico_frame_discard(f);
}
ipv4_cur_frag_id = 0;
if (pico_transport_receive(full, proto) == -1)
{
pico_frame_discard(full);
}
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);
}
static int pico_ipv4_frag_match(struct pico_frame *a, struct pico_frame *b)
{
struct pico_ipv4_hdr *ha, *hb;
if (!a || !b)
return 0;
ha = (struct pico_ipv4_hdr *)a->net_hdr;
hb = (struct pico_ipv4_hdr *)b->net_hdr;
if (!ha || !hb)
return 0;
if (memcmp(&(ha->src.addr), &(hb->src.addr), PICO_SIZE_IP4) != 0)
return 0;
if (memcmp(&(ha->dst.addr), &(hb->dst.addr), PICO_SIZE_IP4) != 0)
return 0;
return 1;
}
#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 int pico_fragments_check_complete(uint8_t proto, uint8_t net)
{
struct pico_tree_node *index, *temp;
struct pico_frame *cur;
unsigned int bookmark = 0;
struct pico_tree *tree = NULL;
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
if (net == PICO_PROTO_IPV4)
{
tree = &ipv4_fragments;
}
#endif
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
if (net == PICO_PROTO_IPV6)
{
tree = &ipv6_fragments;
}
#endif
pico_tree_foreach_safe(index, tree, temp) {
cur = index->keyValue;
if (FRAG_OFF(net, cur->frag) != bookmark)
return 0;
bookmark += cur->transport_len;
if (!FRAG_MORE(net, cur->frag)) {
pico_fragments_complete(bookmark, proto, net);
return 1;
}
}
return 0;
}
static void pico_frag_expire(pico_time now, void *arg)
{
struct pico_tree_node *index, *tmp;
struct pico_frame *f = NULL;
struct pico_tree *tree = (struct pico_tree *) arg;
struct pico_frame *first = NULL;
uint8_t net = 0;
(void)now;
if (!tree)
{
frag_dbg("Expired packet but no tree supplied!\n");
return;
}
first = pico_tree_first(tree);
if (!first) {
frag_dbg("not first - not sending notify\n");
return;
}
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
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)
if (IS_IPV6(first))
{
net = PICO_PROTO_IPV6;
frag_dbg("Packet expired! ID:%hu\n", ipv6_cur_frag_id);
}
#endif
/* Empty the tree */
pico_tree_foreach_safe(index, tree, tmp) {
f = index->keyValue;
pico_tree_delete(tree, f);
if (f != first)
pico_frame_discard(f); /* Later, after ICMP notification...*/
}
if (((FRAG_OFF(net, first->frag) == 0) && (pico_frame_dst_is_unicast(first))))
{
frag_dbg("sending notify\n");
pico_notify_frag_expired(first);
}
if (f)
pico_tree_delete(tree, f);
pico_frame_discard(first);
}
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 = pico_tree_first(&ipv6_fragments);
if (!first) {
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;
}
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 (!first || (pico_ipv6_frag_match(f, first) && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id))) {
pico_tree_insert(&ipv6_fragments, pico_frame_copy(f));
}
pico_fragments_check_complete(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 = pico_tree_first(&ipv4_fragments);
/* fragments from old packets still in tree, and new first fragment ? */
if (first && (IP4_FRAG_ID(hdr) != ipv4_cur_frag_id) && (IP4_FRAG_OFF(f->frag) == 0))
{
/* Empty the tree */
struct pico_tree_node *index, *tmp;
pico_tree_foreach_safe(index, &ipv4_fragments, tmp) {
struct pico_frame * old = index->keyValue;
pico_tree_delete(&ipv4_fragments, old);
pico_frame_discard(old);
}
first = NULL;
ipv4_cur_frag_id = 0;
}
f->frag = short_be(hdr->frag);
if (!first) {
if (ipv4_cur_frag_id && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id)) {
/* Discard late arrivals, without firing the timer */
return;
}
pico_ipv4_frag_timer_on();
ipv4_cur_frag_id = IP4_FRAG_ID(hdr);
}
if (!first || (pico_ipv4_frag_match(f, first) && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id))) {
pico_tree_insert(&ipv4_fragments, pico_frame_copy(f));
}
pico_fragments_check_complete(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,134 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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;
};
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;
}
PICO_TREE_DECLARE(Hotplug_device_tree, pico_hotplug_dev_cmp);
static void timer_cb(__attribute__((unused)) pico_time t, __attribute__((unused)) void* v)
{
struct pico_tree_node *node = NULL, *safe = NULL, *cb_node = NULL, *cb_safe = NULL;
int new_state, event;
struct pico_hotplug_device *hpdev = NULL;
void (*cb)(struct pico_device *dev, int event);
//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 != hpdev->prev_state)
{
if (new_state == 1){
event = PICO_HOTPLUG_EVENT_UP;
} else {
event = PICO_HOTPLUG_EVENT_DOWN;
}
//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;
}
}
timer_id = pico_timer_add(PICO_HOTPLUG_INTERVAL, &timer_cb, NULL);
}
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 (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;
pico_tree_insert(&Hotplug_device_tree, hotplug_dev);
}
pico_tree_insert(&(hotplug_dev->callbacks), cb);
if (timer_id == 0)
{
timer_id = pico_timer_add(PICO_HOTPLUG_INTERVAL, &timer_cb, NULL);
}
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);
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) && timer_id != 0)
{
pico_timer_cancel(timer_id);
timer_id = 0;
}
return 0;
}

View File

@@ -0,0 +1,20 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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
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,395 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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;
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);
}
if (f == NULL) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, (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);
}
PICO_TREE_DECLARE(Pings, cookie_compare);
static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
{
struct pico_frame *echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size));
struct pico_icmp4_hdr *hdr;
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 inline void send_ping(struct pico_icmp4_ping_cookie *cookie)
{
pico_icmp4_send_echo(cookie);
cookie->timestamp = pico_tick;
pico_timer_add((uint32_t)cookie->timeout, ping_timeout, cookie);
if (cookie->seq < (uint16_t)cookie->count)
pico_timer_add((uint32_t)cookie->interval, next_ping, cookie);
}
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++;
pico_tree_insert(&Pings, newcookie);
send_ping(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;
pico_tree_insert(&Pings, cookie);
send_ping(cookie);
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,698 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING for usage.
.
Authors: Kristof Roelants, Daniele Lacamera
*********************************************************************/
#include "pico_config.h"
#include "pico_icmp6.h"
#include "pico_ipv6_nd.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"
#define icmp6_dbg(...) do { }while(0);
static struct pico_queue icmp6_in;
static struct pico_queue icmp6_out;
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, (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;
reply->dev = echo->dev;
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;
#ifdef PICO_SUPPORT_MCAST
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, (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, (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, (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);
notice->dev = f->dev;
/* 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);
}
/* RFC 4861 $7.2.2: sending neighbor solicitations */
int pico_icmp6_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *dst, uint8_t type)
{
struct pico_frame *sol = NULL;
struct pico_icmp6_hdr *icmp6_hdr = NULL;
struct pico_icmp6_opt_lladdr *opt = NULL;
struct pico_ip6 daddr = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00 }};
uint8_t i = 0;
uint16_t len = 0;
if (pico_ipv6_is_multicast(dst->addr))
return -1;
len = PICO_ICMP6HDR_NEIGH_SOL_SIZE;
if (type != PICO_ICMP6_ND_DAD)
len = (uint16_t)(len + 8);
sol = pico_proto_ipv6.alloc(&pico_proto_ipv6, 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_NEIGH_SOL;
icmp6_hdr->code = 0;
icmp6_hdr->msg.info.neigh_sol.unused = 0;
icmp6_hdr->msg.info.neigh_sol.target = *dst;
if (type != PICO_ICMP6_ND_DAD) {
opt = (struct pico_icmp6_opt_lladdr *)(((uint8_t *)&icmp6_hdr->msg.info.neigh_sol) + sizeof(struct neigh_sol_s));
opt->type = PICO_ND_OPT_LLADDR_SRC;
opt->len = 1;
memcpy(opt->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
}
if (type == PICO_ICMP6_ND_SOLICITED || type == PICO_ICMP6_ND_DAD) {
for (i = 1; i <= 3; ++i) {
daddr.addr[PICO_SIZE_IP6 - i] = dst->addr[PICO_SIZE_IP6 - i];
}
} else {
daddr = *dst;
}
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;
}
/* 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, 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);
adv->dev = f->dev;
/* 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_frame *sol = NULL;
struct pico_icmp6_hdr *icmp6_hdr = NULL;
struct pico_icmp6_opt_lladdr *lladdr = NULL;
uint16_t len = 0;
struct pico_ip6 daddr = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }};
len = PICO_ICMP6HDR_ROUTER_SOL_SIZE;
if (!pico_ipv6_is_unspecified(src->addr))
len = (uint16_t)(len + 8);
sol = pico_proto_ipv6.alloc(&pico_proto_ipv6, 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));
lladdr->type = PICO_ND_OPT_LLADDR_SRC;
lladdr->len = 1;
memcpy(lladdr->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
}
sol->dev = dev;
/* f->src is set in frame_push, checksum calculated there */
pico_ipv6_frame_push(sol, NULL, &daddr, PICO_PROTO_ICMP6, 0);
return 0;
}
#define PICO_RADV_VAL_LIFETIME (long_be(86400))
#define PICO_RADV_PREF_LIFETIME (long_be(14400))
/* 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_icmp6_hdr *icmp6_hdr = NULL;
struct pico_icmp6_opt_lladdr *lladdr;
struct pico_icmp6_opt_prefix *prefix;
uint16_t len = 0;
struct pico_ip6 dst_mcast = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }};
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, len);
if (!adv) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
adv->payload = adv->transport_hdr + len;
adv->payload_len = 0;
adv->dev = dev;
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;
memcpy(&prefix->prefix, dst, 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;
lladdr->len = 1;
memcpy(lladdr->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
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_mcast, 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);
}
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, (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));
echo->dev = cookie->dev;
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 inline void pico_icmp6_send_ping(struct pico_icmp6_ping_cookie *cookie)
{
pico_icmp6_send_echo(cookie);
cookie->timestamp = pico_tick;
pico_timer_add((pico_time)(cookie->interval), pico_icmp6_next_ping, cookie);
pico_timer_add((pico_time)(cookie->timeout), pico_icmp6_ping_timeout, cookie);
}
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++;
pico_tree_insert(&IPV6Pings, new);
pico_icmp6_send_ping(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;
pico_tree_insert(&IPV6Pings, cookie);
pico_icmp6_send_ping(cookie);
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,256 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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_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 */
/* 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_MAX_RTR_SOL_DELAY 1000
#define PICO_SIZE_ICMP6HDR ((sizeof(struct pico_icmp6_hdr)))
#define PICO_ICMP6_OPT_LLADDR_SIZE (8)
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[0];
} mld;
} info;
} msg;
};
PACKED_STRUCT_DEF pico_icmp6_opt_lladdr
{
uint8_t type;
uint8_t len;
PACKED_UNION_DEF icmp6_opt_hw_addr_u {
struct pico_eth mac;
} 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;
};
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 *dst, uint8_t type);
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);
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,458 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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)
#define ipf_dbg(...) do {} while(0)
/**************** 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);
};
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->type == 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,129 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING for usage.
.
*********************************************************************/
#ifndef INCLUDE_PICO_IPV4
#define INCLUDE_PICO_IPV4
#include "pico_addressing.h"
#include "pico_protocol.h"
#include "pico_tree.h"
#include "pico_device.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
};
#ifdef PICO_SUPPORT_MCAST
struct pico_mcast_group {
uint8_t filter_mode;
uint16_t reference_count;
struct pico_ip4 mcast_addr;
struct pico_tree MCASTSources;
};
#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_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);
#ifdef __cplusplus
extern "C" {
#endif
int pico_string_to_ipv4(const char *ipstr, uint32_t *ip);
int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask);
#ifdef __cplusplus
}
#endif
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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 */
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;
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;
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;
};
#ifdef __cplusplus
extern "C" {
#endif
struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask);
int pico_string_to_ipv6(const char *ipstr, uint8_t *ip);
#ifdef __cplusplus
}
#endif
int pico_ipv6_compare(struct pico_ip6 *a, struct pico_ip6 *b);
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);
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);
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,26 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING for usage.
*********************************************************************/
#ifndef _INCLUDE_PICO_ND
#define _INCLUDE_PICO_ND
#include "pico_frame.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;
};
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);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
/* ****************************************************************************
* PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved.
* See LICENSE and COPYING 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 4 /* Amount of probes to send */
#define PICO_MDNS_ANNOUNCEMENT_COUNT 3 /* Amount of announcements to send */
/* ****************************************************************************/
#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
/* 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 );
/* ****************************************************************************
* Sets the 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 succ-
* eeded. 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_set_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 LICENSE and COPYING 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[0];
};
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,576 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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
#define nat_dbg(...) do {} while(0)
/* #define nat_dbg dbg */
#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);
}
PICO_TREE_DECLARE(NATOutbound, nat_cmp_outbound);
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++;
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();
pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL);
}
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;
}
nat_link = link;
pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL);
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,266 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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
#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);
}
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);
}
else
{
tmp->state = SLAACV4_ANNOUNCING;
tmp->timer = pico_timer_add(ANNOUNCE_WAIT * 1000, pico_slaacv4_send_announce_timer, arg);
}
}
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);
}
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);
}
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);
}
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;
}
if (dev->link_state(dev) == 1)
{
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);
}
}
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);
}
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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,395 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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
#define sntp_dbg(...) do {} while(0)
/* #define sntp_dbg dbg */
#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: %llu\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: %llusec, %llumsec\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;
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: %llu seconds and %llu 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);
pico_timer_cancel(ck->timer);
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);
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));
}
/* 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;
struct pico_socket *sock;
int retval = -1;
uint16_t any_port = 0;
if(!ck) {
sntp_dbg("dnsCallback: Invalid argument\n");
return;
}
if (0) {
}
#ifdef PICO_SUPPORT_IPV6
else if(ck->proto == PICO_PROTO_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
#ifdef PICO_SUPPORT_IPV4
else if(ck->proto == PICO_PROTO_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) {
sock = pico_socket_open(ck->proto, PICO_PROTO_UDP, &pico_sntp_client_wakeup);
if (!sock)
return;
sock->priv = ck;
ck->sock = sock;
if ((pico_socket_bind(sock, &sntp_inaddr_any, &any_port) == 0)) {
pico_sntp_send(sock, &address);
}
}
}
/* user function to sync the time from a given sntp source */
int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status))
{
struct sntp_server_ns_cookie *ck;
#ifdef PICO_SUPPORT_IPV6
struct sntp_server_ns_cookie *ck6;
#endif
int retval = -1, retval6 = -1;
if (sntp_server == NULL) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
/* 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);
if(cb_synced == NULL) {
PICO_FREE(ck->hostname);
PICO_FREE(ck);
pico_err = PICO_ERR_EINVAL;
return -1;
}
ck->cb_synced = cb_synced;
#ifdef PICO_SUPPORT_IPV6
/* 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;
}
#endif
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;
}
/* 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: %llu seconds and %llu milisecs since 1970\n", tv->tv_sec, tv->tv_msec);
return ret;
}
#endif /* PICO_SUPPORT_SNTP_CLIENT */

View File

@@ -0,0 +1,22 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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_gettimeofday(struct pico_timeval *tv);
#endif /* _INCLUDE_PICO_SNTP_CLIENT */

View File

@@ -0,0 +1,267 @@
#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_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)
break;
} /* FOREACH */
return socket_tcp_do_deliver(found, 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,256 @@
#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;
}
#ifdef PICO_SUPPORT_IPV4
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;
if (pico_enqueue(&s->q_in, cpy) > 0) {
if (s->wakeup)
s->wakeup(PICO_SOCK_EV_RD, s);
}
else
pico_frame_discard(cpy);
}
return 0;
}
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;
if (pico_enqueue(&s->q_in, cpy) > 0) {
if (s->wakeup)
s->wakeup(PICO_SOCK_EV_RD, s);
} else {
pico_frame_discard(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)) {
ret = pico_socket_udp_deliver_ipv4_mcast(s, f);
} 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
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;
}
if (pico_enqueue(&s->q_in, cpy) > 0) {
if (s->wakeup)
s->wakeup(PICO_SOCK_EV_RD, s);
}
else
pico_frame_discard(cpy);
}
return 0;
}
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))) {
int retval = pico_socket_udp_deliver_ipv6_mcast(s, f);
pico_frame_discard(f);
return retval;
}
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;
}
if (pico_enqueue(&s->q_in, cpy) > 0) {
if (s->wakeup)
s->wakeup(PICO_SOCK_EV_RD, s);
}
}
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 Altran ISY BeNeLux. Some rights reserved.
See LICENSE and COPYING 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 1;
}
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 Altran ISY BeNeLux. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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,217 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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"
#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
#define udp_dbg(...) do {} while(0)
/* 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-2015 Altran Intelligent Systems. Some rights reserved.
See LICENSE and COPYING 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