dynamic loading of network stack no longer needed
This commit is contained in:
674
ext/picotcp/modules/pico_aodv.c
Normal file
674
ext/picotcp/modules/pico_aodv.c
Normal 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
|
||||
130
ext/picotcp/modules/pico_aodv.h
Normal file
130
ext/picotcp/modules/pico_aodv.h
Normal 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
|
||||
537
ext/picotcp/modules/pico_arp.c
Normal file
537
ext/picotcp/modules/pico_arp.c
Normal 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);
|
||||
}
|
||||
|
||||
35
ext/picotcp/modules/pico_arp.h
Normal file
35
ext/picotcp/modules/pico_arp.h
Normal 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
|
||||
66
ext/picotcp/modules/pico_dev_loop.c
Normal file
66
ext/picotcp/modules/pico_dev_loop.c
Normal 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;
|
||||
}
|
||||
|
||||
15
ext/picotcp/modules/pico_dev_loop.h
Normal file
15
ext/picotcp/modules/pico_dev_loop.h
Normal 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
|
||||
|
||||
307
ext/picotcp/modules/pico_dev_mock.c
Normal file
307
ext/picotcp/modules/pico_dev_mock.c
Normal 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;
|
||||
}
|
||||
47
ext/picotcp/modules/pico_dev_mock.h
Normal file
47
ext/picotcp/modules/pico_dev_mock.h
Normal 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
|
||||
60
ext/picotcp/modules/pico_dev_null.c
Normal file
60
ext/picotcp/modules/pico_dev_null.c
Normal 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;
|
||||
}
|
||||
|
||||
15
ext/picotcp/modules/pico_dev_null.h
Normal file
15
ext/picotcp/modules/pico_dev_null.h
Normal 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
|
||||
|
||||
96
ext/picotcp/modules/pico_dev_pcap.c
Normal file
96
ext/picotcp/modules/pico_dev_pcap.c
Normal 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);
|
||||
}
|
||||
19
ext/picotcp/modules/pico_dev_pcap.h
Normal file
19
ext/picotcp/modules/pico_dev_pcap.h
Normal 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
|
||||
|
||||
2294
ext/picotcp/modules/pico_dev_ppp.c
Normal file
2294
ext/picotcp/modules/pico_dev_ppp.c
Normal file
File diff suppressed because it is too large
Load Diff
26
ext/picotcp/modules/pico_dev_ppp.h
Normal file
26
ext/picotcp/modules/pico_dev_ppp.h
Normal 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 */
|
||||
220
ext/picotcp/modules/pico_dev_tap.c
Normal file
220
ext/picotcp/modules/pico_dev_tap.c
Normal 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(ð, 0, sizeof(struct ifreq));
|
||||
strcpy(eth.ifr_name, name);
|
||||
/* call the IOCTL */
|
||||
if (ioctl(sck, SIOCGIFHWADDR, ð) < 0) {
|
||||
perror("ioctl(SIOCGIFHWADDR)");
|
||||
return -1;
|
||||
;
|
||||
}
|
||||
|
||||
memcpy (mac, ð.ifr_hwaddr.sa_data, 6);
|
||||
|
||||
|
||||
close(sck);
|
||||
return 0;
|
||||
|
||||
}
|
||||
#else
|
||||
#include <net/if_dl.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if_types.h>
|
||||
static int tap_get_mac(char *name, uint8_t *mac)
|
||||
{
|
||||
struct sockaddr_dl *sdl;
|
||||
struct ifaddrs *ifap, *root;
|
||||
if (getifaddrs(&ifap) != 0)
|
||||
return -1;
|
||||
|
||||
root = ifap;
|
||||
while(ifap) {
|
||||
if (strcmp(name, ifap->ifa_name) == 0)
|
||||
sdl = (struct sockaddr_dl *) ifap->ifa_addr;
|
||||
|
||||
if (sdl->sdl_type == IFT_ETHER) {
|
||||
memcpy(mac, LLADDR(sdl), 6);
|
||||
freeifaddrs(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ifap = ifap->ifa_next;
|
||||
}
|
||||
freeifaddrs(root);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct pico_device *pico_tap_create(char *name)
|
||||
{
|
||||
struct pico_device_tap *tap = PICO_ZALLOC(sizeof(struct pico_device_tap));
|
||||
uint8_t mac[6] = {};
|
||||
struct sigaction sa;
|
||||
|
||||
if (!tap)
|
||||
return NULL;
|
||||
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = sig_handler;
|
||||
|
||||
if ((sigaction(SIGUSR1, &sa, NULL) == 0) &&
|
||||
(sigaction(SIGUSR2, &sa, NULL) == 0))
|
||||
tap->dev.link_state = &tap_link_state;
|
||||
|
||||
tap->dev.overhead = 0;
|
||||
tap->fd = tap_open(name);
|
||||
if (tap->fd < 0) {
|
||||
dbg("Tap creation failed.\n");
|
||||
pico_tap_destroy((struct pico_device *)tap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
15
ext/picotcp/modules/pico_dev_tap.h
Normal file
15
ext/picotcp/modules/pico_dev_tap.h
Normal 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
|
||||
|
||||
1083
ext/picotcp/modules/pico_dev_tap_windows.c
Normal file
1083
ext/picotcp/modules/pico_dev_tap_windows.c
Normal file
File diff suppressed because it is too large
Load Diff
17
ext/picotcp/modules/pico_dev_tap_windows.h
Executable file
17
ext/picotcp/modules/pico_dev_tap_windows.h
Executable 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
|
||||
|
||||
89
ext/picotcp/modules/pico_dev_tap_windows_private.h
Normal file
89
ext/picotcp/modules/pico_dev_tap_windows_private.h
Normal 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
|
||||
110
ext/picotcp/modules/pico_dev_tun.c
Normal file
110
ext/picotcp/modules/pico_dev_tun.c
Normal 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;
|
||||
}
|
||||
|
||||
15
ext/picotcp/modules/pico_dev_tun.h
Normal file
15
ext/picotcp/modules/pico_dev_tun.h
Normal 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
|
||||
|
||||
115
ext/picotcp/modules/pico_dev_vde.c
Normal file
115
ext/picotcp/modules/pico_dev_vde.c
Normal 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;
|
||||
}
|
||||
|
||||
18
ext/picotcp/modules/pico_dev_vde.h
Normal file
18
ext/picotcp/modules/pico_dev_vde.h
Normal 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
|
||||
|
||||
990
ext/picotcp/modules/pico_dhcp_client.c
Normal file
990
ext/picotcp/modules/pico_dhcp_client.c
Normal 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
|
||||
32
ext/picotcp/modules/pico_dhcp_client.h
Normal file
32
ext/picotcp/modules/pico_dhcp_client.h
Normal 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
|
||||
190
ext/picotcp/modules/pico_dhcp_common.c
Normal file
190
ext/picotcp/modules/pico_dhcp_common.c
Normal 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
|
||||
191
ext/picotcp/modules/pico_dhcp_common.h
Normal file
191
ext/picotcp/modules/pico_dhcp_common.h
Normal 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
|
||||
411
ext/picotcp/modules/pico_dhcp_server.c
Normal file
411
ext/picotcp/modules/pico_dhcp_server.c
Normal 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 */
|
||||
34
ext/picotcp/modules/pico_dhcp_server.h
Normal file
34
ext/picotcp/modules/pico_dhcp_server.h
Normal 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
|
||||
808
ext/picotcp/modules/pico_dns_client.c
Normal file
808
ext/picotcp/modules/pico_dns_client.c
Normal 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 */
|
||||
|
||||
46
ext/picotcp/modules/pico_dns_client.h
Normal file
46
ext/picotcp/modules/pico_dns_client.h
Normal 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 */
|
||||
1773
ext/picotcp/modules/pico_dns_common.c
Normal file
1773
ext/picotcp/modules/pico_dns_common.c
Normal file
File diff suppressed because it is too large
Load Diff
523
ext/picotcp/modules/pico_dns_common.h
Normal file
523
ext/picotcp/modules/pico_dns_common.h
Normal 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 */
|
||||
549
ext/picotcp/modules/pico_dns_sd.c
Normal file
549
ext/picotcp/modules/pico_dns_sd.c
Normal 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
|
||||
91
ext/picotcp/modules/pico_dns_sd.h
Normal file
91
ext/picotcp/modules/pico_dns_sd.h
Normal 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 */
|
||||
|
||||
391
ext/picotcp/modules/pico_fragments.c
Normal file
391
ext/picotcp/modules/pico_fragments.c
Normal 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
|
||||
}
|
||||
11
ext/picotcp/modules/pico_fragments.h
Normal file
11
ext/picotcp/modules/pico_fragments.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef PICO_FRAGMENTS_H
|
||||
#define PICO_FRAGMENTS_H
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_frame.h"
|
||||
|
||||
void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto);
|
||||
void pico_ipv4_process_frag(struct pico_ipv4_hdr *hdr, struct pico_frame *f, uint8_t proto);
|
||||
|
||||
#endif
|
||||
134
ext/picotcp/modules/pico_hotplug_detection.c
Normal file
134
ext/picotcp/modules/pico_hotplug_detection.c
Normal 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;
|
||||
}
|
||||
|
||||
20
ext/picotcp/modules/pico_hotplug_detection.h
Normal file
20
ext/picotcp/modules/pico_hotplug_detection.h
Normal 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 */
|
||||
|
||||
395
ext/picotcp/modules/pico_icmp4.c
Normal file
395
ext/picotcp/modules/pico_icmp4.c
Normal 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
|
||||
162
ext/picotcp/modules/pico_icmp4.h
Normal file
162
ext/picotcp/modules/pico_icmp4.h
Normal 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
|
||||
698
ext/picotcp/modules/pico_icmp6.c
Normal file
698
ext/picotcp/modules/pico_icmp6.c
Normal 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
|
||||
256
ext/picotcp/modules/pico_icmp6.h
Normal file
256
ext/picotcp/modules/pico_icmp6.h
Normal 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
|
||||
1276
ext/picotcp/modules/pico_igmp.c
Normal file
1276
ext/picotcp/modules/pico_igmp.c
Normal file
File diff suppressed because it is too large
Load Diff
26
ext/picotcp/modules/pico_igmp.h
Normal file
26
ext/picotcp/modules/pico_igmp.h
Normal 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 */
|
||||
458
ext/picotcp/modules/pico_ipfilter.c
Normal file
458
ext/picotcp/modules/pico_ipfilter.c
Normal 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);
|
||||
}
|
||||
|
||||
29
ext/picotcp/modules/pico_ipfilter.h
Normal file
29
ext/picotcp/modules/pico_ipfilter.h
Normal 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 */
|
||||
|
||||
1585
ext/picotcp/modules/pico_ipv4.c
Normal file
1585
ext/picotcp/modules/pico_ipv4.c
Normal file
File diff suppressed because it is too large
Load Diff
129
ext/picotcp/modules/pico_ipv4.h
Normal file
129
ext/picotcp/modules/pico_ipv4.h
Normal 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 */
|
||||
1921
ext/picotcp/modules/pico_ipv6.c
Normal file
1921
ext/picotcp/modules/pico_ipv6.c
Normal file
File diff suppressed because it is too large
Load Diff
182
ext/picotcp/modules/pico_ipv6.h
Normal file
182
ext/picotcp/modules/pico_ipv6.h
Normal 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
|
||||
1009
ext/picotcp/modules/pico_ipv6_nd.c
Normal file
1009
ext/picotcp/modules/pico_ipv6_nd.c
Normal file
File diff suppressed because it is too large
Load Diff
26
ext/picotcp/modules/pico_ipv6_nd.h
Normal file
26
ext/picotcp/modules/pico_ipv6_nd.h
Normal 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
|
||||
3417
ext/picotcp/modules/pico_mdns.c
Normal file
3417
ext/picotcp/modules/pico_mdns.c
Normal file
File diff suppressed because it is too large
Load Diff
185
ext/picotcp/modules/pico_mdns.h
Normal file
185
ext/picotcp/modules/pico_mdns.h
Normal 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 */
|
||||
1179
ext/picotcp/modules/pico_mld.c
Normal file
1179
ext/picotcp/modules/pico_mld.c
Normal file
File diff suppressed because it is too large
Load Diff
119
ext/picotcp/modules/pico_mld.h
Normal file
119
ext/picotcp/modules/pico_mld.h
Normal 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 */
|
||||
1605
ext/picotcp/modules/pico_mm.c
Normal file
1605
ext/picotcp/modules/pico_mm.c
Normal file
File diff suppressed because it is too large
Load Diff
98
ext/picotcp/modules/pico_mm.h
Normal file
98
ext/picotcp/modules/pico_mm.h
Normal 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 */
|
||||
576
ext/picotcp/modules/pico_nat.c
Normal file
576
ext/picotcp/modules/pico_nat.c
Normal 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
|
||||
90
ext/picotcp/modules/pico_nat.h
Normal file
90
ext/picotcp/modules/pico_nat.h
Normal 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 */
|
||||
|
||||
1143
ext/picotcp/modules/pico_olsr.c
Normal file
1143
ext/picotcp/modules/pico_olsr.c
Normal file
File diff suppressed because it is too large
Load Diff
32
ext/picotcp/modules/pico_olsr.h
Normal file
32
ext/picotcp/modules/pico_olsr.h
Normal 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
|
||||
99
ext/picotcp/modules/pico_posix.c
Normal file
99
ext/picotcp/modules/pico_posix.c
Normal 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 */
|
||||
266
ext/picotcp/modules/pico_slaacv4.c
Normal file
266
ext/picotcp/modules/pico_slaacv4.c
Normal 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
|
||||
18
ext/picotcp/modules/pico_slaacv4.h
Normal file
18
ext/picotcp/modules/pico_slaacv4.h
Normal 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 */
|
||||
|
||||
395
ext/picotcp/modules/pico_sntp_client.c
Normal file
395
ext/picotcp/modules/pico_sntp_client.c
Normal 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 */
|
||||
22
ext/picotcp/modules/pico_sntp_client.h
Normal file
22
ext/picotcp/modules/pico_sntp_client.h
Normal 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 */
|
||||
267
ext/picotcp/modules/pico_socket_tcp.c
Normal file
267
ext/picotcp/modules/pico_socket_tcp.c
Normal 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
|
||||
}
|
||||
33
ext/picotcp/modules/pico_socket_tcp.h
Normal file
33
ext/picotcp/modules/pico_socket_tcp.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef PICO_SOCKET_TCP_H
|
||||
#define PICO_SOCKET_TCP_H
|
||||
#include "pico_socket.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_TCP
|
||||
|
||||
/* Functions/macros: conditional! */
|
||||
|
||||
# define IS_NAGLE_ENABLED(s) (!(!(!(s->opt_flags & (1 << PICO_SOCKET_OPT_TCPNODELAY)))))
|
||||
int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value);
|
||||
int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value);
|
||||
int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f);
|
||||
void pico_socket_tcp_delete(struct pico_socket *s);
|
||||
void pico_socket_tcp_cleanup(struct pico_socket *sock);
|
||||
struct pico_socket *pico_socket_tcp_open(uint16_t family);
|
||||
int pico_socket_tcp_read(struct pico_socket *s, void *buf, uint32_t len);
|
||||
void transport_flags_update(struct pico_frame *, struct pico_socket *);
|
||||
|
||||
#else
|
||||
# define pico_getsockopt_tcp(...) (-1)
|
||||
# define pico_setsockopt_tcp(...) (-1)
|
||||
# define pico_socket_tcp_deliver(...) (-1)
|
||||
# define IS_NAGLE_ENABLED(s) (0)
|
||||
# define pico_socket_tcp_delete(...) do {} while(0)
|
||||
# define pico_socket_tcp_cleanup(...) do {} while(0)
|
||||
# define pico_socket_tcp_open(f) (NULL)
|
||||
# define pico_socket_tcp_read(...) (-1)
|
||||
# define transport_flags_update(...) do {} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
256
ext/picotcp/modules/pico_socket_udp.c
Normal file
256
ext/picotcp/modules/pico_socket_udp.c
Normal 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
|
||||
}
|
||||
|
||||
19
ext/picotcp/modules/pico_socket_udp.h
Normal file
19
ext/picotcp/modules/pico_socket_udp.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef PICO_SOCKET_UDP_H
|
||||
#define PICO_SOCKET_UDP_H
|
||||
|
||||
struct pico_socket *pico_socket_udp_open(void);
|
||||
int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f);
|
||||
|
||||
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
int pico_setsockopt_udp(struct pico_socket *s, int option, void *value);
|
||||
int pico_getsockopt_udp(struct pico_socket *s, int option, void *value);
|
||||
# define pico_socket_udp_recv(s, buf, len, addr, port) pico_udp_recv(s, buf, len, addr, port, NULL)
|
||||
#else
|
||||
# define pico_socket_udp_recv(...) (0)
|
||||
# define pico_getsockopt_udp(...) (-1)
|
||||
# define pico_setsockopt_udp(...) (-1)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
101
ext/picotcp/modules/pico_strings.c
Normal file
101
ext/picotcp/modules/pico_strings.c
Normal 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);
|
||||
}
|
||||
21
ext/picotcp/modules/pico_strings.h
Normal file
21
ext/picotcp/modules/pico_strings.h
Normal 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
|
||||
3236
ext/picotcp/modules/pico_tcp.c
Normal file
3236
ext/picotcp/modules/pico_tcp.c
Normal file
File diff suppressed because it is too large
Load Diff
106
ext/picotcp/modules/pico_tcp.h
Normal file
106
ext/picotcp/modules/pico_tcp.h
Normal 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
|
||||
1300
ext/picotcp/modules/pico_tftp.c
Normal file
1300
ext/picotcp/modules/pico_tftp.c
Normal file
File diff suppressed because it is too large
Load Diff
83
ext/picotcp/modules/pico_tftp.h
Normal file
83
ext/picotcp/modules/pico_tftp.h
Normal 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
|
||||
217
ext/picotcp/modules/pico_udp.c
Normal file
217
ext/picotcp/modules/pico_udp.c
Normal 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;
|
||||
}
|
||||
|
||||
45
ext/picotcp/modules/pico_udp.h
Normal file
45
ext/picotcp/modules/pico_udp.h
Normal 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
|
||||
Reference in New Issue
Block a user