#include "pico_device.h"
#include "pico_dev_ppp.h"
@@ -18,8 +19,11 @@
#include "pico_md5.h"
#include "pico_dns_client.h"
-#define ppp_dbg(...) do {} while(0)
-/* #define ppp_dbg dbg */
+#ifdef DEBUG_PPP
+ #define ppp_dbg dbg
+#else
+ #define ppp_dbg(...) do {} while(0)
+#endif
/* We should define this in a global header. */
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
@@ -125,10 +129,6 @@ PACKED_STRUCT_DEF pico_ipcp_hdr {
uint16_t len;
};
-#ifdef DEBUG_PPP
-static int fifo_fd = -1;
-#endif
-
enum ppp_modem_state {
PPP_MODEM_STATE_INITIAL = 0,
PPP_MODEM_STATE_RESET,
@@ -408,9 +408,9 @@ struct pico_ppp_fsm {
#define LCPOPT_SET_LOCAL(ppp, opt) ppp->lcpopt_local |= (uint16_t)(1u << opt)
#define LCPOPT_SET_PEER(ppp, opt) ppp->lcpopt_peer |= (uint16_t)(1u << opt)
-#define LCPOPT_UNSET_LOCAL(ppp, opt) ppp->lcpopt_local &= (uint16_t)~(1u << opt)
-#define LCPOPT_UNSET_LOCAL_MASK(ppp, opt) ppp->lcpopt_local &= (uint16_t)~(opt)
-#define LCPOPT_UNSET_PEER(ppp, opt) ppp->lcpopt_peer &= (uint16_t)~(1u << opt)
+#define LCPOPT_UNSET_LOCAL(ppp, opt) ppp->lcpopt_local &= (uint16_t) ~(1u << opt)
+#define LCPOPT_UNSET_LOCAL_MASK(ppp, opt) ppp->lcpopt_local &= (uint16_t) ~(opt)
+#define LCPOPT_UNSET_PEER(ppp, opt) ppp->lcpopt_peer &= (uint16_t) ~(1u << opt)
#define LCPOPT_ISSET_LOCAL(ppp, opt) ((ppp->lcpopt_local & (uint16_t)(1u << opt)) != 0)
#define LCPOPT_ISSET_PEER(ppp, opt) ((ppp->lcpopt_peer & (uint16_t)(1u << opt)) != 0)
@@ -855,9 +855,9 @@ static void lcp_optflags_print(struct pico_device_ppp *ppp, uint8_t *opts, uint3
int i;
ppp_dbg("-- LCP opt: %d - len: %d - data:", p[0], p[1]);
- for (i=0; imru = (uint16_t)((p[2] << 8) + p[3]);
- break;
- case LCPOPT_AUTH:
- ppp_dbg("Setting AUTH to %02x%02x\n", p[2], p[3]);
- ppp->auth = (uint16_t)((p[2] << 8) + p[3]);
- break;
- default:
- break;
+ case LCPOPT_MRU:
+ /* XXX: Can we accept any MRU ? */
+ ppp_dbg("Adjusting MRU to %02x%02x\n", p[2], p[3]);
+ ppp->mru = (uint16_t)((p[2] << 8) + p[3]);
+ break;
+ case LCPOPT_AUTH:
+ ppp_dbg("Setting AUTH to %02x%02x\n", p[2], p[3]);
+ ppp->auth = (uint16_t)((p[2] << 8) + p[3]);
+ break;
+ default:
+ break;
}
}
@@ -904,7 +904,7 @@ static uint16_t lcp_optflags(struct pico_device_ppp *ppp, uint8_t *pkt, uint32_t
p += off;
}
#ifdef PPP_DEBUG
- lcp_optflags_print(ppp, pkt + sizeof(struct pico_lcp_hdr), (uint32_t)(len - sizeof(struct pico_lcp_hdr)) );
+ lcp_optflags_print(ppp, pkt + sizeof(struct pico_lcp_hdr), (uint32_t)(len - sizeof(struct pico_lcp_hdr)));
#endif
return flags;
}
@@ -982,9 +982,9 @@ static void lcp_send_configure_nack(struct pico_device_ppp *ppp)
dst_opts[dstopts_len++] = p[1];
ppp_dbg("data: ");
- for(i = 0; i < p[1]-2u; i++) { /* length includes type, length and data fields */
+ for(i = 0; i < p[1] - 2u; i++) { /* length includes type, length and data fields */
dst_opts[dstopts_len++] = p[2 + i];
- ppp_dbg("%02X ", p[2+i]);
+ ppp_dbg("%02X ", p[2 + i]);
}
ppp_dbg("\n");
}
@@ -1014,6 +1014,7 @@ static void lcp_process_in(struct pico_device_ppp *ppp, uint8_t *pkt, uint32_t l
uint16_t optflags;
if (!ppp)
return;
+
if (pkt[0] == PICO_CONF_REQ) {
uint16_t rejected = 0;
ppp_dbg("Received LCP CONF REQ\n");
@@ -1171,12 +1172,16 @@ static void ipcp_request_fill(struct pico_device_ppp *ppp, uint8_t *opts)
{
if (ppp->ipcp_allowed_fields & IPCP_ALLOW_IP)
opts += ipcp_request_add_address(opts, IPCP_OPT_IP, ppp->ipcp_ip);
+
if (ppp->ipcp_allowed_fields & IPCP_ALLOW_DNS1)
opts += ipcp_request_add_address(opts, IPCP_OPT_DNS1, ppp->ipcp_dns1);
+
if (ppp->ipcp_allowed_fields & IPCP_ALLOW_DNS2)
opts += ipcp_request_add_address(opts, IPCP_OPT_DNS2, ppp->ipcp_dns2);
+
if ((ppp->ipcp_allowed_fields & IPCP_ALLOW_NBNS1) && (ppp->ipcp_nbns1))
opts += ipcp_request_add_address(opts, IPCP_OPT_NBNS1, ppp->ipcp_nbns1);
+
if ((ppp->ipcp_allowed_fields & IPCP_ALLOW_NBNS2) && (ppp->ipcp_nbns2))
opts += ipcp_request_add_address(opts, IPCP_OPT_NBNS2, ppp->ipcp_nbns2);
}
@@ -1427,6 +1432,7 @@ static void ppp_recv_data(struct pico_device_ppp *ppp, void *data, uint32_t len)
}
ppp_dbg("\n");
}
+
#endif
ppp_process_packet(ppp, pkt, len);
@@ -1743,7 +1749,7 @@ static void auth_abort(struct pico_device_ppp *ppp)
static void auth_req(struct pico_device_ppp *ppp)
{
- uint16_t ppp_usr_len = 0;
+ uint16_t ppp_usr_len = 0;
uint16_t ppp_pwd_len = 0;
uint8_t *req = NULL, *p;
struct pico_pap_hdr *hdr;
@@ -1783,15 +1789,16 @@ static void auth_req(struct pico_device_ppp *ppp)
memcpy(p, ppp->password, ppp_pwd_len);
p += ppp_pwd_len;
}
+
ppp_dbg("PAP: Sending authentication request.\n");
pico_ppp_ctl_send(&ppp->dev, PPP_PROTO_PAP,
- req, /* Start of PPP packet */
- (uint32_t)(
- PPP_HDR_SIZE + PPP_PROTO_SLOT_SIZE + /* PPP Header, etc. */
- pap_len + /* Authentication packet len */
- PPP_FCS_SIZE + /* FCS */
- 1) /* STOP Byte */
- );
+ req, /* Start of PPP packet */
+ (uint32_t)(
+ PPP_HDR_SIZE + PPP_PROTO_SLOT_SIZE + /* PPP Header, etc. */
+ pap_len + /* Authentication packet len */
+ PPP_FCS_SIZE + /* FCS */
+ 1) /* STOP Byte */
+ );
PICO_FREE(req);
}
@@ -2161,7 +2168,10 @@ static void pico_ppp_tick(pico_time t, void *arg)
evaluate_lcp_state(ppp, PPP_LCP_EVENT_OPEN);
}
- pico_timer_add(1000, pico_ppp_tick, arg);
+ if (!pico_timer_add(1000, pico_ppp_tick, arg)) {
+ ppp_dbg("PPP: Failed to start tick timer\n");
+ /* TODO No more PPP ticks now */
+ }
}
struct pico_device *pico_ppp_create(void)
@@ -2191,6 +2201,11 @@ struct pico_device *pico_ppp_create(void)
ppp->ipcp_state = PPP_IPCP_STATE_INITIAL;
ppp->timer = pico_timer_add(1000, pico_ppp_tick, ppp);
+ if (!ppp->timer) {
+ ppp_dbg("PPP: Failed to start tick timer\n");
+ pico_device_destroy((struct pico_device*) ppp);
+ return NULL;
+ }
ppp->mru = PICO_PPP_MRU;
LCPOPT_SET_LOCAL(ppp, LCPOPT_MRU);
@@ -2215,6 +2230,9 @@ int pico_ppp_disconnect(struct pico_device *dev)
struct pico_device_ppp *ppp = (struct pico_device_ppp *)dev;
ppp->autoreconnect = 0;
evaluate_lcp_state(ppp, PPP_LCP_EVENT_CLOSE);
+
+ pico_ipv4_cleanup_links(dev);
+
return 0;
}
diff --git a/ext/picotcp/modules/pico_dev_ppp.h b/ext/picotcp/modules/pico_dev_ppp.h
index 6110e50..5d479c4 100644
--- a/ext/picotcp/modules/pico_dev_ppp.h
+++ b/ext/picotcp/modules/pico_dev_ppp.h
@@ -1,6 +1,6 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- See LICENSE and COPYING for usage.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef INCLUDE_PICO_PPP
diff --git a/ext/picotcp/modules/pico_dev_radio_mgr.c b/ext/picotcp/modules/pico_dev_radio_mgr.c
new file mode 100644
index 0000000..162caed
--- /dev/null
+++ b/ext/picotcp/modules/pico_dev_radio_mgr.c
@@ -0,0 +1,357 @@
+/*********************************************************************
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See LICENSE and COPYING for usage.
+
+ Authors: Jelle De Vleeschouwer
+ *********************************************************************/
+
+/*
+ * For testing purposes. pico_dev_radio_manager allows simulating a mesh
+ * network for smoke tests. I previously used geomess, but that's another
+ * dependency to add then. Then @danielinux wrote the pico_dev_radiotest but
+ * that required adding a multicast route on the host which in its turn
+ * required 'sudo'. So I wrote a small simulator which doesn't require sudo.
+ * - Jelle
+ */
+
+#include "pico_dev_radiotest.h"
+#include "pico_addressing.h"
+#include "pico_dev_tap.h"
+#include "pico_802154.h"
+#include "pico_device.h"
+#include "pico_config.h"
+#include "pico_stack.h"
+#include "pico_dev_radio_mgr.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef DEBUG_RADIOTEST
+#define RADIO_DBG dbg
+#else
+#define RADIO_DBG(...) do { } while (0)
+#endif
+
+#define LISTENING_PORT 7777
+#define MESSAGE_MTU 150
+#define EVER (;;)
+
+struct socket {
+ int s;
+ uint8_t mgr;
+ uint8_t id;
+ uint8_t area0;
+ uint8_t area1;
+};
+
+/* Compare two application sockets */
+static int
+pico_radio_mgr_sock_cmp(void *a, void *b)
+{
+ struct socket *sa = a, *sb = b;
+ return (int)(sa->id - sb->id);
+}
+
+PICO_TREE_DECLARE(Sockets, pico_radio_mgr_sock_cmp);
+
+/* Insert a new socket in the tree */
+static int
+pico_radio_mgr_socket_insert(int socket, uint8_t id, uint8_t area0, uint8_t area1, uint8_t mgr)
+{
+ struct socket *s = PICO_ZALLOC(sizeof(struct socket));
+ if (s) {
+ s->area0 = area0;
+ s->area1 = area1;
+ s->s = socket;
+ s->mgr = mgr;
+ s->id = id;
+ if (!pico_tree_insert(&Sockets, s))
+ return 0;
+ PICO_FREE(s);
+ }
+ return -1;
+}
+
+/* Gather an array of poll descriptors with all sockets */
+static struct pollfd *
+pico_radio_mgr_socket_all(int *n)
+{
+ struct pico_tree_node *i = NULL;
+ struct socket *key = NULL;
+ struct pollfd *fds = NULL;
+ int j = 1;
+ *n = 0;
+
+ /* Retrieve all sockets */
+ pico_tree_foreach(i, &Sockets) {
+ (*n)++;
+ }
+
+ /* Create array from tree */
+ fds = PICO_ZALLOC(sizeof(struct pollfd) * (size_t)*n);
+ if (fds) {
+ /* Put every socket in array */
+ pico_tree_foreach(i, &Sockets) {
+ if (i && (key = i->keyValue)) {
+ if (!key->id) {
+ fds[0].fd = key->s;
+ fds[0].events = POLLIN;
+ } else {
+ fds[j].fd = key->s;
+ fds[j].events = POLLIN | POLLHUP;
+ j++;
+ }
+ }
+ }
+ }
+
+ return fds;
+}
+
+/* Get connection socket that belongs to a particular node */
+static struct socket *
+pico_radio_mgr_socket_node(uint8_t id)
+{
+ struct socket test = { 0, 0, id };
+ return pico_tree_findKey(&Sockets, &test);
+}
+
+/* Handle POLLHUP event */
+static int
+pico_radio_mgr_socket_hup(int socket)
+{
+ struct pico_tree_node *i = NULL;
+ struct socket *key = NULL;
+
+ pico_tree_foreach(i, &Sockets) {
+ key = i->keyValue;
+ if (key && key->s == socket) {
+ pico_tree_delete(&Sockets, key);
+ RADIO_DBG("Radio %d detached from network\n", key->id);
+ PICO_FREE(key);
+ close(socket);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/* Receive's an 'Hello'-message from the node that contains the id, the inserts
+ * an entry in the Sockets-tree */
+static int
+pico_radio_mgr_welcome(int socket)
+{
+ int ret_len = sizeof(uint8_t);
+ uint8_t id = 0, area0, area1;
+
+ errno = 0;
+ while ((ret_len = recv(socket, &id, (size_t)ret_len, 0)) != 1) {
+ if (errno && EINTR != errno)
+ goto hup;
+ }
+ while ((ret_len = recv(socket, &area0, (size_t)ret_len, 0)) != 1) {
+ if (errno && EINTR != errno)
+ goto hup;
+ }
+ while ((ret_len = recv(socket, &area1, (size_t)ret_len, 0)) != 1) {
+ if (errno && EINTR != errno)
+ goto hup;
+ }
+
+ if (id <= 0) { // Node's can't have ID '0'.
+ RADIO_DBG("Invalid socket\n");
+ close(socket);
+ return -1;
+ }
+
+ RADIO_DBG("Connected to node %u in area %u and %u on socket %d.\n", id, area0, area1, socket);
+ if (pico_radio_mgr_socket_insert(socket, id, area0, area1, 0)) {
+ RADIO_DBG("Failed inserting new socket\n");
+ close(socket);
+ return -1;
+ }
+
+ return 0;
+hup:
+ RADIO_DBG("recv() failed with error: %s\n", strerror(errno));
+ close(socket);
+ return -1;
+}
+
+/* Accepts a new TCP connection request */
+static int
+pico_radio_mgr_accept(int socket)
+{
+ unsigned int len = sizeof(struct sockaddr_in);
+ struct sockaddr_in addr;
+ int s = accept(socket, (struct sockaddr *)&addr, &len);
+ if (s < 0) {
+ RADIO_DBG("Failed accepting connection\n");
+ return s;
+ } else if (!s) {
+ RADIO_DBG("accept() returned file descriptor '%d'\n", s);
+ return s;
+ }
+ return pico_radio_mgr_welcome(s);
+}
+
+/* Start listening for TCP connection requests on 'LISTENING_PORT' */
+static int
+pico_radio_mgr_listen(void)
+{
+ struct sockaddr_in addr;
+ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ int ret = 0, yes = 1;
+
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(LISTENING_PORT);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
+
+ ret = bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+ if (ret < 0) {
+ RADIO_DBG("Failed binding socket to address: %s\n", strerror(ret));
+ return -1;
+ }
+
+ ret = listen(s, 5);
+ if (ret < 0) {
+ RADIO_DBG("Failed start listening\n");
+ return -1;
+ }
+
+ /* Also insert server socket in tree for polling */
+ if (pico_radio_mgr_socket_insert(s, 0, 0, 0, 1)) {
+ close(s);
+ return -1;
+ }
+
+ dbg("Started listening on port %d\n", LISTENING_PORT);
+ return s;
+}
+
+/* Distribute received frame over all the areas where the node is attached to */
+static void
+pico_radio_mgr_distribute(uint8_t *buf, int len, uint8_t id)
+{
+ struct socket *node = pico_radio_mgr_socket_node(id);
+ uint8_t area0 = 0, area1 = 0, ar0 = 0, ar1 = 0, phy = (uint8_t)len;
+ struct pico_tree_node *i = NULL;
+ struct socket *key = NULL;
+ if (node) {
+ RADIO_DBG("Received frame from node '%d' of '%d' bytes\n", id, len);
+ area0 = node->area0;
+ area1 = node->area1;
+ } else {
+ RADIO_DBG("Received frame from node not connected to network, weird..\n");
+ return;
+ }
+
+ pico_tree_foreach(i, &Sockets) {
+ key = i->keyValue;
+ if (key && key->id != id && key->id) { // Do not sent to ourselves or manager
+ ar0 = key->area0;
+ ar1 = key->area1;
+ if (area0 == ar0 || area0 == ar1 || (area1 && (area1 == ar0 || area1 == ar1))) {
+ len = (int)sendto(key->s, &phy, (size_t)1, 0, NULL, 0);
+ if (len != 1) return;
+ len = (int)sendto(key->s, buf, (size_t)phy, 0, NULL, 0);
+ if (len == (int)phy)
+ RADIO_DBG("Forwarded from '%u' of %d bytes sent to '%u'\n", id, len, key->id);
+ }
+ }
+ }
+}
+
+/* Process poll-events */
+static void
+pico_radio_mgr_process(struct pollfd *fds, int n)
+{
+ uint8_t buf[MESSAGE_MTU] = { 0 }, node = 0, phy = 0;
+ int i = 0, ret_len = 0;
+ short event = 0;
+
+ for (i = 0; i < n; i++) {
+ event = fds[i].revents;
+ if (event && (event & POLLIN)) { // POLLIN
+ if (!i) {
+ /* Accept a new connection */
+ pico_radio_mgr_accept(fds[i].fd);
+ continue;
+ }
+
+ /* Read from node */
+ ret_len = (int)recv(fds[i].fd, &phy, (size_t)1, 0);
+ if (ret_len <= 0)
+ goto hup;
+ ret_len = (int)recv(fds[i].fd, buf, (size_t)phy, 0);
+ if (ret_len <= 0 || ret_len != phy)
+ goto hup;
+ node = buf[ret_len - 2];
+ pico_radio_mgr_distribute(buf, ret_len, node);
+ } else if (event && (event & POLLHUP)) {
+ goto hup;
+ }
+ }
+
+ return;
+hup:
+ pico_radio_mgr_socket_hup(fds[i].fd);
+}
+
+static void
+pico_radio_mgr_quit(int signum)
+{
+ struct pico_tree_node *i = NULL, *tmp = NULL;
+ struct socket *key = NULL;
+ IGNORE_PARAMETER(signum);
+
+ dbg("Closing all sockets...");
+ pico_tree_foreach_safe(i, &Sockets, tmp) {
+ key = i->keyValue;
+ if (key) {
+ pico_tree_delete(&Sockets, key);
+ shutdown(key->s, SHUT_RDWR);
+ PICO_FREE(key);
+ }
+ }
+ dbg("done.\n");
+ exit(0);
+}
+
+/* Create and start a radio-manager instance */
+int
+pico_radio_mgr_start(void)
+{
+ int server = pico_radio_mgr_listen();
+ struct pollfd *fds = NULL;
+ nfds_t n = 0;
+ int ret = 0;
+ if (server < 0)
+ return -1;
+
+ signal(SIGQUIT, pico_radio_mgr_quit);
+
+ for EVER {
+ if (fds)
+ PICO_FREE(fds);
+ fds = pico_radio_mgr_socket_all((int *)&n);
+ errno = 0;
+ ret = poll(fds, n, 1);
+ if (errno != EINTR && ret < 0) {
+ RADIO_DBG("Socket error: %s\n", strerror(ret));
+ return ret;
+ } else if (!ret) {
+ continue;
+ }
+ pico_radio_mgr_process(fds, (int)n);
+ }
+}
diff --git a/ext/picotcp/modules/pico_dev_radio_mgr.h b/ext/picotcp/modules/pico_dev_radio_mgr.h
new file mode 100644
index 0000000..37da415
--- /dev/null
+++ b/ext/picotcp/modules/pico_dev_radio_mgr.h
@@ -0,0 +1,14 @@
+/*********************************************************************
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See LICENSE and COPYING for usage.
+
+ Authors: Jelle De Vleeschouwer
+ *********************************************************************/
+
+#ifndef __PICO_DEV_RADIO_MGR_H_
+#define __PICO_DEV_RADIO_MGR_H_
+
+/* Start listening for TCP connection requests on 'LISTENING_PORT' */
+int pico_radio_mgr_start(void);
+
+#endif
diff --git a/ext/picotcp/modules/pico_dev_radiotest.c b/ext/picotcp/modules/pico_dev_radiotest.c
new file mode 100644
index 0000000..a1b75be
--- /dev/null
+++ b/ext/picotcp/modules/pico_dev_radiotest.c
@@ -0,0 +1,486 @@
+/*********************************************************************
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See LICENSE and COPYING for usage.
+
+ Authors: Daniele Lacamera, Jelle De Vleeschouwer
+ *********************************************************************/
+
+/*******************************************************************************
+ * PicoTCP
+ ******************************************************************************/
+
+#include "pico_dev_radiotest.h"
+#include "pico_6lowpan_ll.h"
+#include "pico_addressing.h"
+#include "pico_dev_tap.h"
+#include "pico_802154.h"
+#include "pico_device.h"
+#include "pico_config.h"
+#include "pico_stack.h"
+
+/*******************************************************************************
+ * System sockets
+ ******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LISTENING_PORT 7777
+#define MESSAGE_MTU 150
+
+#ifdef RADIO_PCAP
+#include
+#endif
+
+#ifdef DEBUG_RADIOTEST
+#define RADIO_DBG dbg
+#else
+#define RADIO_DBG(...) do { } while (0)
+#endif
+
+/*******************************************************************************
+ * Constants
+ ******************************************************************************/
+
+/* Uncomment next line to enable Random packet loss (specify percentage) */
+//#define PACKET_LOSS 3
+
+#define RFDEV_PANID 0xABCD
+#define MC_ADDR_BE 0x010101EBU
+#define LO_ADDR 0x0100007FU
+
+#define LOOP_MTU 127
+
+/*******************************************************************************
+ * Type definitions
+ ******************************************************************************/
+
+struct radiotest_radio {
+ struct pico_dev_6lowpan dev;
+ struct pico_6lowpan_info addr;
+ int sock0;
+ int sock1;
+#ifdef RADIO_PCAP
+ pcap_t *pcap;
+ pcap_dumper_t *pcapd;
+#endif
+};
+
+struct radiotest_frame
+{
+ uint8_t *buf;
+ int len;
+ uint32_t id;
+ union pico_ll_addr src;
+ union pico_ll_addr dst;
+};
+
+/*******************************************************************************
+ * Global variables
+ ******************************************************************************/
+
+static int connection = 0;
+
+static uint32_t tx_id = 0;
+static uint32_t rx_id = 0;
+
+/*******************************************************************************
+ * pcap
+ ******************************************************************************/
+
+#ifdef RADIO_PCAP
+
+static void radiotest_pcap_open(struct radiotest_radio *dev, char *dump)
+{
+ char path[100];
+
+ /* Open offline packet capture */
+#ifdef PICO_SUPPORT_802154
+ dev->pcap = pcap_open_dead(DLT_IEEE802_15_4, 65535);
+#elif defined (PICO_SUPPORT_802154_NO_MAC)
+ dev->pcap = pcap_open_dead(DLT_RAW, 65535);
+#endif
+ if (!dev->pcap) {
+ perror("LibPCAP");
+ exit (1);
+ }
+
+ /* Construct file path */
+ snprintf(path, 100, dump, dev->addr);
+
+ /* Open dump */
+ dev->pcapd = pcap_dump_open(dev->pcap, path);
+ if (dev->pcapd)
+ dbg("PCAP Enabled\n");
+ else
+ dbg("PCAP Disabled\n");
+}
+
+static void radiotest_pcap_write(struct radiotest_radio *dev, uint8_t *buf, int len)
+{
+ struct pcap_pkthdr ph;
+ if (!dev || !dev->pcapd)
+ return;
+ ph.caplen = (uint32_t)len;
+ ph.len = (uint32_t)len;
+ gettimeofday(&ph.ts, NULL);
+ pcap_dump((u_char *)dev->pcapd, &ph, buf);
+ pcap_dump_flush(dev->pcapd);
+}
+
+#else
+
+static void radiotest_pcap_open(struct radiotest_radio *dev, char *dump)
+{
+ (void)dev;
+ (void)dump;
+}
+
+static void radiotest_pcap_write(struct radiotest_radio *dev, uint8_t *buf, int len)
+{
+ (void)dev;
+ (void)buf;
+ (void)len;
+}
+
+#endif
+
+static int radiotest_cmp(void *a, void *b)
+{
+ struct radiotest_frame *fa = (struct radiotest_frame *)a;
+ struct radiotest_frame *fb = (struct radiotest_frame *)b;
+ return (int)(fa->id - fb->id);
+}
+
+PICO_TREE_DECLARE(LoopFrames, radiotest_cmp);
+
+static uint8_t *radiotest_nxt_rx(int *len, union pico_ll_addr *src, union pico_ll_addr *dst)
+{
+ struct radiotest_frame test, *found = NULL;
+ uint8_t *ret = NULL;
+ test.id = rx_id++;
+
+ found = pico_tree_findKey(&LoopFrames, &test);
+ if (found) {
+ ret = found->buf;
+ *len = found->len;
+ *src = found->src;
+ *dst = found->dst;
+ pico_tree_delete(&LoopFrames, found);
+ PICO_FREE(found);
+ } else {
+ rx_id--;
+ }
+ return ret;
+}
+
+static void radiotest_nxt_tx(uint8_t *buf, int len, union pico_ll_addr src, union pico_ll_addr dst)
+{
+ struct radiotest_frame *new = PICO_ZALLOC(sizeof(struct radiotest_frame));
+ if (new) {
+ new->buf = PICO_ZALLOC((uint16_t)len);
+ if (new->buf) {
+ memcpy(new->buf, buf, (size_t)len);
+ new->len = len;
+ new->id = tx_id++;
+ new->src = src;
+ new->dst = dst;
+ if (pico_tree_insert(&LoopFrames, new)) {
+ PICO_FREE(new);
+ tx_id--;
+ }
+ } else {
+ PICO_FREE(new);
+ }
+ }
+}
+
+static int pico_loop_send(struct pico_device *dev, void *buf, int len, union pico_ll_addr src, union pico_ll_addr dst)
+{
+ IGNORE_PARAMETER(dev);
+ if (len > LOOP_MTU)
+ return 0;
+ RADIO_DBG("Looping back frame of %d bytes.\n", len);
+ radiotest_nxt_tx(buf, len, src, dst);
+ return len;
+}
+
+static int pico_loop_poll(struct pico_device *dev, int loop_score)
+{
+ union pico_ll_addr src, dst;
+ uint8_t *buf = NULL;
+ int len = 0;
+
+ if (loop_score <= 0)
+ return 0;
+
+ buf = radiotest_nxt_rx(&len, &src, &dst);
+ if (buf) {
+ RADIO_DBG("Receiving frame of %d bytes.\n", len);
+ pico_6lowpan_stack_recv(dev, buf, (uint32_t)len, &src, &dst);
+ PICO_FREE(buf);
+ loop_score--;
+ }
+
+ return loop_score;
+}
+
+
+/* Generates a simple extended address */
+static void radiotest_gen_ex(struct pico_6lowpan_short addr_short, uint8_t *buf)
+{
+ uint16_t sh = addr_short.addr;
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ buf[3] = 0xaa;
+ buf[4] = 0xab;
+ buf[5] = 0x00;
+ buf[6] = (uint8_t)((uint8_t)(short_be(sh) & 0xFF00) >> 8u);
+ buf[7] = (uint8_t)(short_be(sh) & 0xFFu);
+}
+
+/**
+ * Simulated CRC16-CITT Kermit generation
+ *
+ * @param buf uint8_t *, buffer to generate FCS for.
+ * @param len uint8_t, len of the buffer
+ *
+ * @return CITT Kermit CRC16 of the buffer
+ */
+static uint16_t calculate_crc16(uint8_t *buf, uint8_t len)
+{
+ uint16_t crc = 0x0000;
+ uint16_t q = 0, i = 0;
+ uint8_t c = 0;
+
+ for (i = 0; i < len; i++) {
+ c = buf[i];
+ q = (crc ^ c) & 0x0F;
+ crc = (uint16_t)((uint16_t)(crc >> 4) ^ (q * 0x1081));
+ q = (crc ^ (c >> 4)) & 0xF;
+ crc = (uint16_t)((uint16_t)(crc >> 4) ^ (q * 0x1081));
+ }
+
+ return crc;
+}
+
+/* Poll-function for the pico_device-structure */
+static int radiotest_poll(struct pico_device *dev, int loop_score)
+{
+ struct radiotest_radio *radio = (struct radiotest_radio *)dev;
+ union pico_ll_addr src = {0}, dst = {0};
+ int pollret, ret_len;
+ struct pollfd p;
+ uint8_t buf[128];
+ uint8_t phy = 0;
+
+ if (loop_score <= 0)
+ return 0;
+
+ if (!dev)
+ return loop_score;
+
+ p.fd = connection;
+ p.events = POLLIN | POLLHUP;
+
+ /* Poll for data from radio management */
+ errno = 0;
+ pollret = poll(&p, (nfds_t)1, 1);
+ if (errno == EINTR || pollret == 0)
+ return loop_score;
+
+ if (pollret < 0) {
+ fprintf(stderr, "Socket error %s!\n", strerror(errno));
+ exit(5);
+ }
+
+ if (p.revents & POLLIN) {
+ ret_len = (int)recv(connection, &phy, (size_t)1, 0);
+ if (ret_len != 1) return loop_score;
+ ret_len = (int)recv(connection, buf, (size_t)phy, 0);
+ if (ret_len != (int)phy)
+ return loop_score;
+ else if (!ret_len) {
+ RADIO_DBG("Radio manager detached from network\n");
+ exit(1);
+ }
+ }
+
+ if (ret_len < 2) { /* Not valid */
+ return loop_score;
+ }
+
+#ifdef P_LOSS
+ long n = lrand48();
+ n = n % 100;
+ if (n < P_LOSS) {
+ RADIO_DBG("Packet got lost!\n");
+ return loop_score;
+ }
+#endif
+
+ /* ADDRESS FILTER */
+ if (buf[ret_len - 1] != 0xFF && buf[ret_len - 1] != (uint8_t)short_be(radio->addr.addr_short.addr)) {
+ RADIO_DBG("Packet is not for me!\n");
+ return loop_score;
+ }
+
+ /* Get src and destination address */
+ dst.pan.addr._ext = radio->addr.addr_ext;
+ src.pan.addr.data[3] = 0xAA;
+ src.pan.addr.data[4] = 0xAB;
+ src.pan.addr.data[7] = buf[ret_len - 1];
+ src.pan.mode = AM_6LOWPAN_EXT;
+ ret_len -= 2;
+
+ /* Write the received frame to the pcap-dump */
+ radiotest_pcap_write(radio, buf, ret_len);
+
+ /* Hand the frame over to pico */
+ pico_6lowpan_stack_recv(dev, buf, (uint32_t)(ret_len - 2), &src, &dst);
+ loop_score--;
+
+ return loop_score;
+}
+
+#define RADIO_OVERHEAD 4
+
+/* Send-function for the pico_device-structure */
+static int radiotest_send(struct pico_device *dev, void *_buf, int len, union pico_ll_addr src, union pico_ll_addr dst)
+{
+ struct radiotest_radio *radio = (struct radiotest_radio *)dev;
+ uint8_t *buf = PICO_ZALLOC((size_t)(len + RADIO_OVERHEAD));
+ uint8_t phy = 0, did = 0;
+ uint16_t crc = 0;
+ int ret = 0, dlen = 0;
+ IGNORE_PARAMETER(src);
+
+ if (!buf)
+ return -1;
+
+ /* Try to get node-ID from address */
+ if (dev && pico_6lowpan_lls[dev->mode].addr_len) {
+ dlen = pico_6lowpan_lls[dev->mode].addr_len(&dst);
+ if (dlen < 0)
+ return -1;
+ did = dst.pan.addr.data[dlen - 1];
+ }
+
+ /* Store the addresses in buffer for management */
+ memcpy(buf, _buf, (size_t)len);
+ len = (uint16_t)(len + (uint16_t)RADIO_OVERHEAD); // CRC + ID
+ buf[len - 2] = (uint8_t)short_be(radio->addr.addr_short.addr);
+ buf[len - 1] = did;
+
+ /* Generate FCS, keep pcap happy ... */
+ crc = calculate_crc16(_buf, (uint8_t)(len - RADIO_OVERHEAD));
+ memcpy(buf + len - RADIO_OVERHEAD, (void *)&crc, 2);
+
+ /* Send frame to radio management */
+ phy = (uint8_t)(len);
+ ret = (int)sendto(connection, &phy, 1, 0, NULL, 0);
+ if (ret != 1)
+ return -1;
+ ret = (int)sendto(connection, buf, (size_t)(len), 0, NULL, 0);
+ RADIO_DBG("Radio '%u' transmitted a frame of %d bytes.\n", buf[len - 2], ret);
+
+ /* Write the sent frame to the pcap-dump */
+ radiotest_pcap_write(radio, buf, len - 2);
+
+ PICO_FREE(buf);
+ return ret;
+}
+
+static int radiotest_hello(int s, uint8_t id, uint8_t area0, uint8_t area1)
+{
+ uint8_t buf[3] = { id, area0, area1 };
+ if (sendto(s, buf, (size_t)3, 0, NULL, 0) != 3) {
+ RADIO_DBG("Radio '%u' failed to send hello message\n", id);
+ return -1;
+ }
+
+ RADIO_DBG("Radio '%u' attached to network\n", id);
+ return s;
+}
+
+static int radiotest_connect(uint8_t id, uint8_t area0, uint8_t area1)
+{
+ struct sockaddr_in addr;
+ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ int ret = 0;
+
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(LISTENING_PORT);
+ inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
+
+ ret = connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+ if (ret) {
+ RADIO_DBG("Radio '%u' could not attach to network\n", id);
+ return ret;
+ }
+
+ return radiotest_hello(s, id, area0, area1);
+}
+
+static void
+pico_radiotest_quit(int signum)
+{
+ IGNORE_PARAMETER(signum);
+ dbg("Quitting radiotest\n");
+ exit(0);
+}
+
+/* Creates a radiotest-device */
+struct pico_device *pico_radiotest_create(uint8_t addr, uint8_t area0, uint8_t area1, int loop, char *dump)
+{
+ struct radiotest_radio *radio = PICO_ZALLOC(sizeof(struct radiotest_radio));
+ struct pico_dev_6lowpan *lp = (struct pico_dev_6lowpan *)radio;
+ if (!radio)
+ return NULL;
+ if (!addr || (addr && !area0)) {
+ RADIO_DBG("Usage (node): -6 [1-255],[1-255],[0-255] ...\n");
+ }
+
+ signal(SIGQUIT, pico_radiotest_quit);
+
+ radio->addr.pan_id.addr = short_be(RFDEV_PANID);
+ radio->addr.addr_short.addr = short_be((uint16_t)addr);
+ radiotest_gen_ex(radio->addr.addr_short, radio->addr.addr_ext.addr);
+ RADIO_DBG("Radiotest short address: 0x%04X\n", short_be(radio->addr.addr_short.addr));
+ RADIO_DBG("Radiotest ext address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ radio->addr.addr_ext.addr[0],radio->addr.addr_ext.addr[1],
+ radio->addr.addr_ext.addr[2],radio->addr.addr_ext.addr[3],
+ radio->addr.addr_ext.addr[4],radio->addr.addr_ext.addr[5],
+ radio->addr.addr_ext.addr[6],radio->addr.addr_ext.addr[7]);
+
+ if (!loop) {
+ if ((connection = radiotest_connect(addr, area0, area1)) <= 0) {
+ return NULL;
+ }
+ if (pico_dev_6lowpan_init(lp, "radio", (uint8_t *)&radio->addr, LL_MODE_IEEE802154, MTU_802154_MAC, 0, radiotest_send, radiotest_poll)) {
+ RADIO_DBG("pico_device_init failed.\n");
+ pico_device_destroy((struct pico_device *)lp);
+ return NULL;
+ }
+ } else {
+ if (pico_dev_6lowpan_init(lp, "radio", (uint8_t *)&radio->addr, LL_MODE_IEEE802154, MTU_802154_MAC, 0, pico_loop_send, pico_loop_poll)) {
+ RADIO_DBG("pico_device_init failed.\n");
+ pico_device_destroy((struct pico_device *)lp);
+ return NULL;
+ }
+ }
+
+ if (dump) {
+ dbg("Dump: %s\n", dump);
+ radiotest_pcap_open(radio, dump);
+ }
+
+ return (struct pico_device *)lp;
+}
+
diff --git a/ext/picotcp/modules/pico_dev_radiotest.h b/ext/picotcp/modules/pico_dev_radiotest.h
new file mode 100644
index 0000000..eb484ee
--- /dev/null
+++ b/ext/picotcp/modules/pico_dev_radiotest.h
@@ -0,0 +1,16 @@
+/*********************************************************************
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See LICENSE and COPYING for usage.
+
+ Authors: Daniele Lacamera, Jelle De Vleeschouwer
+ *********************************************************************/
+
+#ifndef INCLUDE_PICO_DEV_RADIOTEST
+#define INCLUDE_PICO_DEV_RADIOTEST
+
+#include "pico_device.h"
+#include "pico_config.h"
+
+struct pico_device *pico_radiotest_create(uint8_t addr, uint8_t area0, uint8_t area1, int loop, char *dump);
+
+#endif /* INCLUDE_PICO_DEV_RADIOTEST */
diff --git a/ext/picotcp/modules/pico_dev_tap.c b/ext/picotcp/modules/pico_dev_tap.c
index b80ea6d..877941f 100644
--- a/ext/picotcp/modules/pico_dev_tap.c
+++ b/ext/picotcp/modules/pico_dev_tap.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
*********************************************************************/
@@ -27,23 +27,26 @@ struct pico_device_tap {
#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.
+/* 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 int tapdev_link_state = 0;
static void sig_handler(int signo)
{
- if (signo == SIGUSR1)
- link_state = 0;
- if (signo == SIGUSR2)
- link_state = 1;
+ if (signo == SIGUSR1) {
+ tapdev_link_state = 0;
+ }
+
+ if (signo == SIGUSR2) {
+ tapdev_link_state = 1;
+ }
}
static int tap_link_state(__attribute__((unused)) struct pico_device *self)
{
- return link_state;
+ return tapdev_link_state;
}
@@ -62,8 +65,9 @@ static int pico_tap_poll(struct pico_device *dev, int loop_score)
pfd.fd = tap->fd;
pfd.events = POLLIN;
do {
- if (poll(&pfd, 1, 0) <= 0)
+ if (poll(&pfd, 1, 0) <= 0) {
return loop_score;
+ }
len = (int)read(tap->fd, buf, TUN_MTU);
if (len > 0) {
@@ -79,8 +83,9 @@ static int pico_tap_poll(struct pico_device *dev, int loop_score)
void pico_tap_destroy(struct pico_device *dev)
{
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
- if(tap->fd > 0)
+ if(tap->fd > 0) {
close(tap->fd);
+ }
}
#ifndef __FreeBSD__
@@ -89,14 +94,14 @@ static int tap_open(char *name)
struct ifreq ifr;
int tap_fd;
if((tap_fd = open("/dev/net/tun", O_RDWR)) < 0) {
- return(-1);
+ 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 -1;
}
return tap_fd;
@@ -119,9 +124,6 @@ static int tap_get_mac(char *name, uint8_t *mac)
struct ifreq eth;
int retval = -1;
-
-
-
sck = socket(AF_INET, SOCK_DGRAM, 0);
if(sck < 0) {
return retval;
@@ -133,12 +135,9 @@ static int tap_get_mac(char *name, uint8_t *mac)
if (ioctl(sck, SIOCGIFHWADDR, ð) < 0) {
perror("ioctl(SIOCGIFHWADDR)");
return -1;
- ;
}
memcpy (mac, ð.ifr_hwaddr.sa_data, 6);
-
-
close(sck);
return 0;
@@ -156,8 +155,9 @@ static int tap_get_mac(char *name, uint8_t *mac)
root = ifap;
while(ifap) {
- if (strcmp(name, ifap->ifa_name) == 0)
+ 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);
@@ -178,16 +178,18 @@ struct pico_device *pico_tap_create(char *name)
uint8_t mac[6] = {};
struct sigaction sa;
- if (!tap)
+ 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;
+ (sigaction(SIGUSR2, &sa, NULL) == 0)) {
+ tap->dev.link_state = &tap_link_state;
+ }
tap->dev.overhead = 0;
tap->fd = tap_open(name);
@@ -197,12 +199,20 @@ struct pico_device *pico_tap_create(char *name)
return NULL;
}
+ /* Host's mac address is generated * by the host kernel and is
+ * retrieved via tap_get_mac().
+ */
if (tap_get_mac(name, mac) < 0) {
dbg("Tap mac query failed.\n");
pico_tap_destroy((struct pico_device *)tap);
return NULL;
}
+ /* To act as a second endpoint in the same subnet, the picoTCP
+ * app using the tap device must have a different mac address.
+ * For simplicity, we just add 1 to the last byte of the linux
+ * endpoint so the two addresses are consecutive.
+ */
mac[5]++;
if( 0 != pico_device_init((struct pico_device *)tap, name, mac)) {
diff --git a/ext/picotcp/modules/pico_dev_tap.h b/ext/picotcp/modules/pico_dev_tap.h
index ebc21fe..384226e 100644
--- a/ext/picotcp/modules/pico_dev_tap.h
+++ b/ext/picotcp/modules/pico_dev_tap.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef INCLUDE_PICO_TAP
diff --git a/ext/picotcp/modules/pico_dev_tap_windows.c b/ext/picotcp/modules/pico_dev_tap_windows.c
index 2e003f8..252f32a 100644
--- a/ext/picotcp/modules/pico_dev_tap_windows.c
+++ b/ext/picotcp/modules/pico_dev_tap_windows.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2014-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Maxime Vincent
Based on the OpenVPN tun.c driver, under GPL
@@ -28,10 +28,29 @@
/*
* Debugging info
*/
-#define dbg_tap_info(...) /* tap info messages */
-#define dbg_tap(...) /* first level debug */
-#define dbg_win32(...) /* second level detailed win32 debug */
-#define dbg_reg(...) /* third level: registry debug */
+#ifdef DEBUG_TAP_GENERAL
+#define dbg_tap dbg /* first level debug */
+#else
+#define dbg_tap(...) do{} while(0)
+#endif
+
+#ifdef DEBUG_TAP_INFO
+#define dbg_tap_info dbg /* tap info messages */
+#else
+#define dbg_tap_info(...) do{} while(0)
+#endif
+
+#ifdef DEBUG_TAP_WIN
+#define dbg_tap_win32 dbg /* second level detailed win32 debug */
+#else
+#define dbg_tap_win32(...) do{} while(0)
+#endif
+
+#ifdef DEBUG_TAP_REG
+#define dbg_tap_reg dbg /* third level: registry debug */
+#else
+#define dbg_tap_reg(...) do{} while(0)
+#endif
/*
* Tunnel types
@@ -123,7 +142,7 @@ const struct tap_reg *get_tap_reg (void)
if (status != ERROR_SUCCESS)
{
- dbg_reg("Error opening registry key: %s\n", ADAPTER_KEY);
+ dbg_tap_reg("Error opening registry key: %s\n", ADAPTER_KEY);
return NULL;
}
@@ -151,7 +170,7 @@ const struct tap_reg *get_tap_reg (void)
if (status == ERROR_NO_MORE_ITEMS)
break;
else if (status != ERROR_SUCCESS)
- dbg_reg("Error enumerating registry subkeys of key: %s.\n", ADAPTER_KEY);
+ dbg_tap_reg("Error enumerating registry subkeys of key: %s.\n", ADAPTER_KEY);
snprintf (unit_string, sizeof(unit_string), "%s\\%s",
ADAPTER_KEY, enum_name);
@@ -165,7 +184,7 @@ const struct tap_reg *get_tap_reg (void)
if (status != ERROR_SUCCESS)
{
- dbg_reg("Error opening registry key: %s\n", unit_string);
+ dbg_tap_reg("Error opening registry key: %s\n", unit_string);
}
else
{
@@ -180,7 +199,7 @@ const struct tap_reg *get_tap_reg (void)
if (status != ERROR_SUCCESS || data_type != REG_SZ)
{
- dbg_reg("Error opening registry key: %s\\%s\n", unit_string, component_id_string);
+ dbg_tap_reg("Error opening registry key: %s\\%s\n", unit_string, component_id_string);
}
else
{
@@ -252,7 +271,7 @@ const struct panel_reg *get_panel_reg (void)
if (status != ERROR_SUCCESS)
{
- dbg_reg("Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY);
+ dbg_tap_reg("Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY);
return NULL;
}
@@ -278,7 +297,7 @@ const struct panel_reg *get_panel_reg (void)
if (status == ERROR_NO_MORE_ITEMS)
break;
else if (status != ERROR_SUCCESS)
- dbg_reg("Error enumerating registry subkeys of key: %s.\n", NETWORK_CONNECTIONS_KEY);
+ dbg_tap_reg("Error enumerating registry subkeys of key: %s.\n", NETWORK_CONNECTIONS_KEY);
snprintf (connection_string, sizeof(connection_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name);
@@ -289,7 +308,7 @@ const struct panel_reg *get_panel_reg (void)
KEY_READ,
&connection_key);
if (status != ERROR_SUCCESS)
- dbg_reg("Error opening registry key: %s\n", connection_string);
+ dbg_tap_reg("Error opening registry key: %s\n", connection_string);
else
{
len = sizeof (name_data);
@@ -302,7 +321,7 @@ const struct panel_reg *get_panel_reg (void)
&len);
if (status != ERROR_SUCCESS || name_type != REG_SZ)
- dbg_reg("Error opening registry key: %s\\%s\\%S\n", NETWORK_CONNECTIONS_KEY, connection_string, name_string);
+ dbg_tap_reg("Error opening registry key: %s\\%s\\%S\n", NETWORK_CONNECTIONS_KEY, connection_string, name_string);
else
{
int n;
@@ -689,7 +708,7 @@ int tun_read_queue (struct tuntap *tt, uint8_t *buffer, int maxsize)
tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
tt->reads.status = 0;
- dbg_win32 ("WIN32 I/O: TAP Read immediate return [%d,%d]\n",
+ dbg_tap_win32 ("WIN32 I/O: TAP Read immediate return [%d,%d]\n",
(int) len,
(int) tt->reads.size);
}
@@ -700,7 +719,7 @@ int tun_read_queue (struct tuntap *tt, uint8_t *buffer, int maxsize)
{
tt->reads.iostate = IOSTATE_QUEUED;
tt->reads.status = err;
- dbg_win32 ("WIN32 I/O: TAP Read queued [%d]\n", (int) len);
+ dbg_tap_win32 ("WIN32 I/O: TAP Read queued [%d]\n", (int) len);
}
else /* error occurred */
{
@@ -747,7 +766,7 @@ int tun_finalize(HANDLE h, struct overlapped_io *io, uint8_t **buf, uint32_t *bu
if (!ResetEvent (io->overlapped.hEvent))
dbg_tap("ResetEvent in finalize failed!\n");
- dbg_win32 ("WIN32 I/O: TAP Completion success: QUEUED! [%d]\n", ret);
+ dbg_tap_win32 ("WIN32 I/O: TAP Completion success: QUEUED! [%d]\n", ret);
}
else
{
@@ -788,7 +807,7 @@ int tun_finalize(HANDLE h, struct overlapped_io *io, uint8_t **buf, uint32_t *bu
*buf = io->buf;
ret = io->size;
- dbg_win32 ("WIN32 I/O: TAP Completion non-queued success [%d]\n", ret);
+ dbg_tap_win32 ("WIN32 I/O: TAP Completion non-queued success [%d]\n", ret);
}
break;
@@ -846,7 +865,7 @@ int tun_write_queue (struct tuntap *tt, uint8_t *buf, uint32_t buf_len)
tt->writes.status = 0;
- dbg_win32 ("WIN32 I/O: TAP Write immediate return [%d,%d]\n",
+ dbg_tap_win32 ("WIN32 I/O: TAP Write immediate return [%d,%d]\n",
(int)(tt->writes.buf_len),
(int)tt->writes.size);
}
@@ -857,7 +876,7 @@ int tun_write_queue (struct tuntap *tt, uint8_t *buf, uint32_t buf_len)
{
tt->writes.iostate = IOSTATE_QUEUED;
tt->writes.status = err;
- dbg_win32("WIN32 I/O: TAP Write queued [%d]\n",
+ dbg_tap_win32("WIN32 I/O: TAP Write queued [%d]\n",
(tt->writes.buf_len));
}
else /* error occurred */
@@ -1080,4 +1099,3 @@ struct pico_device *pico_tap_create(char *name, uint8_t *mac)
return (struct pico_device *)tap;
}
-
diff --git a/ext/picotcp/modules/pico_dev_tap_windows.h b/ext/picotcp/modules/pico_dev_tap_windows.h
old mode 100755
new mode 100644
index 25bd4fc..bdc6977
--- a/ext/picotcp/modules/pico_dev_tap_windows.h
+++ b/ext/picotcp/modules/pico_dev_tap_windows.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2014-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef INCLUDE_PICO_TAP
diff --git a/ext/picotcp/modules/pico_dev_tap_windows_private.h b/ext/picotcp/modules/pico_dev_tap_windows_private.h
index 8f3b192..ef52354 100644
--- a/ext/picotcp/modules/pico_dev_tap_windows_private.h
+++ b/ext/picotcp/modules/pico_dev_tap_windows_private.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2014-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Maxime Vincent
Based on the OpenVPN tun.c driver, under GPL
diff --git a/ext/picotcp/modules/pico_dev_tun.c b/ext/picotcp/modules/pico_dev_tun.c
index c5791d6..148ad92 100644
--- a/ext/picotcp/modules/pico_dev_tun.c
+++ b/ext/picotcp/modules/pico_dev_tun.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_dev_tun.h b/ext/picotcp/modules/pico_dev_tun.h
index 4dd477e..85b9dbf 100644
--- a/ext/picotcp/modules/pico_dev_tun.h
+++ b/ext/picotcp/modules/pico_dev_tun.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef INCLUDE_PICO_TUN
diff --git a/ext/picotcp/modules/pico_dev_vde.c b/ext/picotcp/modules/pico_dev_vde.c
index df07a3b..3358162 100644
--- a/ext/picotcp/modules/pico_dev_vde.c
+++ b/ext/picotcp/modules/pico_dev_vde.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
*********************************************************************/
@@ -26,6 +26,13 @@ struct pico_device_vde {
#define VDE_MTU 65536
+/* Mockables */
+#if defined UNIT_TEST
+# define MOCKABLE __attribute__((weak))
+#else
+# define MOCKABLE
+#endif
+
static int pico_vde_send(struct pico_device *dev, void *buf, int len)
{
struct pico_device_vde *vde = (struct pico_device_vde *) dev;
@@ -80,7 +87,7 @@ void pico_vde_set_packetloss(struct pico_device *dev, uint32_t in_pct, uint32_t
-struct pico_device *pico_vde_create(char *sock, char *name, uint8_t *mac)
+struct pico_device *MOCKABLE pico_vde_create(char *sock, char *name, uint8_t *mac)
{
struct pico_device_vde *vde = PICO_ZALLOC(sizeof(struct pico_device_vde));
struct vde_open_args open_args = {
diff --git a/ext/picotcp/modules/pico_dev_vde.h b/ext/picotcp/modules/pico_dev_vde.h
index c6c999e..4b13087 100644
--- a/ext/picotcp/modules/pico_dev_vde.h
+++ b/ext/picotcp/modules/pico_dev_vde.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_dhcp_client.c b/ext/picotcp/modules/pico_dhcp_client.c
index f834dd6..5b56962 100644
--- a/ext/picotcp/modules/pico_dhcp_client.c
+++ b/ext/picotcp/modules/pico_dhcp_client.c
@@ -1,8 +1,8 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
- Authors: Kristof Roelants, Frederik Van Slycken
+ Authors: Kristof Roelants, Frederik Van Slycken, Maxime Vincent
*********************************************************************/
@@ -15,8 +15,12 @@
#include "pico_eth.h"
#if (defined PICO_SUPPORT_DHCPC && defined PICO_SUPPORT_UDP)
-#define dhcpc_dbg(...) do {} while(0)
-/* #define dhcpc_dbg dbg */
+
+#ifdef DEBUG_DHCP_CLIENT
+ #define dhcpc_dbg dbg
+#else
+ #define dhcpc_dbg(...) do {} while(0)
+#endif
/* timer values */
#define DHCP_CLIENT_REINIT 6000 /* msec */
@@ -30,6 +34,13 @@
#define DHCP_CLIENT_MAXMSGZISE (PICO_IP_MRU - PICO_SIZE_IP4HDR)
#define PICO_DHCP_HOSTNAME_MAXLEN 64U
+/* Mockables */
+#if defined UNIT_TEST
+# define MOCKABLE __attribute__((weak))
+#else
+# define MOCKABLE
+#endif
+
static char dhcpc_host_name[PICO_DHCP_HOSTNAME_MAXLEN] = "";
static char dhcpc_domain_name[PICO_DHCP_HOSTNAME_MAXLEN] = "";
@@ -60,6 +71,7 @@ struct dhcp_client_timer
uint8_t state;
unsigned int type;
uint32_t xid;
+ uint32_t timer_id;
};
struct pico_dhcp_client_cookie
@@ -91,6 +103,7 @@ 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 void pico_dhcp_client_callback(struct pico_dhcp_client_cookie *dhcpc, int code);
static const struct pico_ip4 bcast_netmask = {
.addr = 0xFFFFFFFF
@@ -109,7 +122,7 @@ static int dhcp_cookies_cmp(void *ka, void *kb)
return (a->xid < b->xid) ? (-1) : (1);
}
-PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp);
+static 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)
{
@@ -137,7 +150,11 @@ static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid,
dhcpc->cb = cb;
dhcpc->dev = dev;
- pico_tree_insert(&DHCPCookies, dhcpc);
+ if (pico_tree_insert(&DHCPCookies, dhcpc)) {
+ PICO_FREE(dhcpc);
+ return NULL;
+ }
+
return dhcpc;
}
@@ -180,20 +197,30 @@ 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;
+ struct dhcp_client_timer *t = ck->timer[type];
+ if (t) {
+ /* Stale timer, mark to be freed in the callback */
+ t->state = DHCP_CLIENT_TIMER_STOPPED;
+ }
+
+ /* allocate a new timer, the old one is still in the timer tree, and will be freed as soon as it expires */
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;
+ t->timer_id = pico_timer_add(time, pico_dhcp_client_timer_handler, t);
+ if (!t->timer_id) {
+ dhcpc_dbg("DHCP: Failed to start timer\n");
+ PICO_FREE(t);
+ return NULL;
}
+ /* store timer struct reference in cookie */
ck->timer[type] = t;
return t;
}
@@ -234,28 +261,25 @@ static void pico_dhcp_client_timer_handler(pico_time now, void *arg)
(void) now;
if (t->state != DHCP_CLIENT_TIMER_STOPPED) {
dhcpc = pico_dhcp_client_find_cookie(t->xid);
- if (dhcpc && dhcpc->timer) {
+ if (dhcpc) {
t->state = DHCP_CLIENT_TIMER_STOPPED;
if ((t->type == PICO_DHCPC_TIMER_INIT) && (dhcpc->state < DHCP_CLIENT_STATE_SELECTING)) {
+ /* this was an INIT timer */
pico_dhcp_client_reinit(now, dhcpc);
} else if (t->type != PICO_DHCPC_TIMER_INIT) {
+ /* this was NOT an INIT timer */
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);
}
}
}
+
+ /* stale timer, it's associated struct should be freed */
+ if (t->state == DHCP_CLIENT_TIMER_STOPPED)
+ PICO_FREE(t);
}
-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;
@@ -268,8 +292,7 @@ static void pico_dhcp_client_reinit(pico_time now, void *arg)
if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
pico_err = PICO_ERR_EAGAIN;
- if (dhcpc->cb)
- dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+ pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR);
pico_dhcp_client_del_cookie(dhcpc->xid);
return;
@@ -285,27 +308,39 @@ 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);
+ {
+ if (dhcpc->timer[i]) {
+ /* Do not cancel timer, but rather set it's state to be freed when it expires */
+ dhcpc->timer[i]->state = DHCP_CLIENT_TIMER_STOPPED;
+ dhcpc->timer[i] = NULL;
+ }
+ }
}
-static void pico_dhcp_client_start_init_timer(struct pico_dhcp_client_cookie *dhcpc)
+static int 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);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, time * 1000, dhcpc))
+ return -1;
+
+ return 0;
}
-static void pico_dhcp_client_start_requesting_timer(struct pico_dhcp_client_cookie *dhcpc)
+static int 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);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_REQUEST, time * 1000, dhcpc))
+ return -1;
+
+ return 0;
}
-static void pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie *dhcpc)
+static int pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie *dhcpc)
{
uint32_t halftime = 0;
@@ -316,12 +351,13 @@ static void pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie
if (halftime < 60)
halftime = 60;
- pico_dhcp_timer_add(PICO_DHCPC_TIMER_RENEW, halftime * 1000, dhcpc);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_RENEW, halftime * 1000, dhcpc))
+ return -1;
- return;
+ return 0;
}
-static void pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cookie *dhcpc)
+static int pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cookie *dhcpc)
{
uint32_t halftime = 0;
@@ -330,18 +366,30 @@ static void pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cooki
if (halftime < 60)
halftime = 60;
- pico_dhcp_timer_add(PICO_DHCPC_TIMER_REBIND, halftime * 1000, dhcpc);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_REBIND, halftime * 1000, dhcpc))
+ return -1;
- return;
+ return 0;
}
-static void pico_dhcp_client_start_reacquisition_timers(struct pico_dhcp_client_cookie *dhcpc)
+static int 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);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_T1, dhcpc->t1_time * 1000, dhcpc))
+ goto fail;
+
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_T2, dhcpc->t2_time * 1000, dhcpc))
+ goto fail;
+
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_LEASE, dhcpc->lease_time * 1000, dhcpc))
+ goto fail;
+
+ return 0;
+
+fail:
+ pico_dhcp_client_stop_timers(dhcpc);
+ return -1;
}
static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc)
@@ -357,7 +405,9 @@ static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc)
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);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc))
+ return -1;
+
return 0;
}
@@ -365,24 +415,33 @@ static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc)
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);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc))
+ return -1;
+
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);
+ if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc))
+ return -1;
+
return 0;
}
dhcpc->retry = 0;
dhcpc->init_timestamp = PICO_TIME_MS();
- pico_dhcp_client_start_init_timer(dhcpc);
+ if (pico_dhcp_client_start_init_timer(dhcpc) < 0) {
+ pico_socket_close(dhcpc->s);
+ dhcpc->s = NULL;
+ return -1;
+ }
+
return 0;
}
-int pico_dhcp_initiate_negotiation(struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
+int MOCKABLE 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;
@@ -478,22 +537,24 @@ static void pico_dhcp_client_recv_params(struct pico_dhcp_client_cookie *dhcpc,
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;
+ }
+ 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;
+ }
+ break;
default:
dhcpc_dbg("DHCP client: WARNING unsupported option %u\n", opt->code);
@@ -514,7 +575,7 @@ static void pico_dhcp_client_recv_params(struct pico_dhcp_client_cookie *dhcpc,
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);
+ 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)
@@ -526,21 +587,42 @@ static int recv_offer(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
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);
+ if (pico_dhcp_client_start_requesting_timer(dhcpc) < 0)
+ return -1;
+
return 0;
}
+static void pico_dhcp_client_update_link(struct pico_dhcp_client_cookie *dhcpc)
+{
+ struct pico_ip4 any_address = {
+ 0
+ };
+ struct pico_ip4 address = {
+ 0
+ };
+ struct pico_ipv4_link *l;
+
+ dbg("DHCP client: update link\n");
+
+ 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);
+
+ /* 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);
+ }
+}
+
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_dhcp_opt *opt = DHCP_OPT(hdr, 0);
struct pico_ipv4_link *l;
pico_dhcp_client_recv_params(dhcpc, opt);
@@ -557,32 +639,28 @@ static int recv_ack(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
pico_socket_close(dhcpc->s);
dhcpc->s = NULL;
- /* Delete all the links before adding the address */
- pico_ipv4_link_del(dhcpc->dev, address);
+ /* Delete all the links before adding the new ip address
+ * in case the new address doesn't match the old one */
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);
+ if (dhcpc->address.addr != (l->address).addr) {
+ pico_dhcp_client_update_link(dhcpc);
}
- 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);
+ if (pico_dhcp_client_start_reacquisition_timers(dhcpc) < 0) {
+ pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR);
+ return -1;
+ }
+
*(dhcpc->uid) = dhcpc->xid;
- if (dhcpc->cb)
- dhcpc->cb(dhcpc, PICO_DHCP_SUCCESS);
+ pico_dhcp_client_callback(dhcpc, PICO_DHCP_SUCCESS);
dhcpc->state = DHCP_CLIENT_STATE_BOUND;
return 0;
@@ -596,8 +674,7 @@ static int renew(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
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);
+ pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR);
return -1;
}
@@ -606,15 +683,20 @@ static int renew(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
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);
+ pico_dhcp_client_callback(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);
+ if (pico_dhcp_client_start_renewing_timer(dhcpc) < 0) {
+ pico_socket_close(dhcpc->s);
+ dhcpc->s = NULL;
+ pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR);
+
+ return -1;
+ }
return 0;
}
@@ -626,7 +708,8 @@ static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t *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);
+ if (pico_dhcp_client_start_rebinding_timer(dhcpc) < 0)
+ return -1;
return 0;
}
@@ -649,8 +732,7 @@ static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
/* 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);
+ pico_dhcp_client_callback(dhcpc, PICO_DHCP_RESET);
if (dhcpc->state < DHCP_CLIENT_STATE_BOUND)
{
@@ -671,28 +753,33 @@ static int retransmit(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
{
case DHCP_CLIENT_STATE_INIT:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
- pico_dhcp_client_start_init_timer(dhcpc);
+ if (pico_dhcp_client_start_init_timer(dhcpc) < 0)
+ return -1;
break;
case DHCP_CLIENT_STATE_REQUESTING:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
- pico_dhcp_client_start_requesting_timer(dhcpc);
+ if (pico_dhcp_client_start_requesting_timer(dhcpc) < 0)
+ return -1;
break;
case DHCP_CLIENT_STATE_RENEWING:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
- pico_dhcp_client_start_renewing_timer(dhcpc);
+ if (pico_dhcp_client_start_renewing_timer(dhcpc) < 0)
+ return -1;
break;
case DHCP_CLIENT_STATE_REBINDING:
pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
- pico_dhcp_client_start_rebinding_timer(dhcpc);
+ if (pico_dhcp_client_start_rebinding_timer(dhcpc) < 0)
+ return -1;
break;
default:
dhcpc_dbg("DHCP client WARNING: retransmit in incorrect state (%u)!\n", dhcpc->state);
return -1;
}
+
return 0;
}
@@ -708,8 +795,7 @@ struct dhcp_action_entry {
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 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 },
@@ -719,6 +805,12 @@ static struct dhcp_action_entry dhcp_fsm[] =
/* state rebinding */ { NULL, recv_ack, reset, NULL, NULL, reset, retransmit },
};
+static void dhcp_action_call( int (*call)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf), struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+ if (call)
+ call(dhcpc, buf);
+}
+
/* 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
@@ -732,51 +824,37 @@ static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cooki
{
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);
-
+ dhcp_action_call(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);
-
+ dhcp_action_call(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);
-
+ dhcp_action_call(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);
-
+ dhcp_action_call(dhcp_fsm[dhcpc->state].timer1, dhcpc, buf);
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);
-
+ dhcp_action_call(dhcp_fsm[dhcpc->state].timer2, dhcpc, buf);
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);
-
+ dhcp_action_call(dhcp_fsm[dhcpc->state].timer_lease, dhcpc, buf);
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);
-
+ dhcp_action_call(dhcp_fsm[dhcpc->state].timer_retransmit, dhcpc, buf);
break;
default:
@@ -790,7 +868,7 @@ 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);
+ struct pico_dhcp_opt *opt = DHCP_OPT(hdr, 0);
if (hdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE)
return -1;
@@ -833,7 +911,7 @@ static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_
}
/* specific options */
- offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(DHCP_OPT(hdr,offset), DHCP_CLIENT_MAXMSGZISE));
+ offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(DHCP_OPT(hdr, offset), DHCP_CLIENT_MAXMSGZISE));
break;
case PICO_DHCP_MSG_REQUEST:
@@ -846,10 +924,10 @@ static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_
}
/* specific options */
- offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(DHCP_OPT(hdr,offset), DHCP_CLIENT_MAXMSGZISE));
+ 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));
+ 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;
@@ -859,9 +937,9 @@ static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_
}
/* 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)));
+ 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)
{
@@ -940,17 +1018,23 @@ out_discard_buf:
PICO_FREE(buf);
}
-void *pico_dhcp_get_identifier(uint32_t xid)
+static void pico_dhcp_client_callback(struct pico_dhcp_client_cookie *dhcpc, int code)
+{
+ if(dhcpc->cb)
+ dhcpc->cb(dhcpc, code);
+}
+
+void *MOCKABLE pico_dhcp_get_identifier(uint32_t xid)
{
return (void *)pico_dhcp_client_find_cookie(xid);
}
-struct pico_ip4 pico_dhcp_get_address(void*dhcpc)
+struct pico_ip4 MOCKABLE pico_dhcp_get_address(void*dhcpc)
{
return ((struct pico_dhcp_client_cookie*)dhcpc)->address;
}
-struct pico_ip4 pico_dhcp_get_gateway(void*dhcpc)
+struct pico_ip4 MOCKABLE pico_dhcp_get_gateway(void*dhcpc)
{
return ((struct pico_dhcp_client_cookie*)dhcpc)->gateway;
}
diff --git a/ext/picotcp/modules/pico_dhcp_client.h b/ext/picotcp/modules/pico_dhcp_client.h
index 4dafb2a..8ec8e27 100644
--- a/ext/picotcp/modules/pico_dhcp_client.h
+++ b/ext/picotcp/modules/pico_dhcp_client.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
diff --git a/ext/picotcp/modules/pico_dhcp_common.c b/ext/picotcp/modules/pico_dhcp_common.c
index 7edb1e2..ccd15f2 100644
--- a/ext/picotcp/modules/pico_dhcp_common.c
+++ b/ext/picotcp/modules/pico_dhcp_common.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -153,7 +153,7 @@ 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;
diff --git a/ext/picotcp/modules/pico_dhcp_common.h b/ext/picotcp/modules/pico_dhcp_common.h
index 62c94da..4ccdc67 100644
--- a/ext/picotcp/modules/pico_dhcp_common.h
+++ b/ext/picotcp/modules/pico_dhcp_common.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -16,7 +16,7 @@
#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))
+#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
diff --git a/ext/picotcp/modules/pico_dhcp_server.c b/ext/picotcp/modules/pico_dhcp_server.c
index dbef3bb..03ce387 100644
--- a/ext/picotcp/modules/pico_dhcp_server.c
+++ b/ext/picotcp/modules/pico_dhcp_server.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Frederik Van Slycken, Kristof Roelants
@@ -16,8 +16,11 @@
#if (defined PICO_SUPPORT_DHCPD && defined PICO_SUPPORT_UDP)
-#define dhcps_dbg(...) do {} while(0)
-/* #define dhcps_dbg dbg */
+#ifdef DEBUG_DHCP_SERVER
+ #define dhcps_dbg dbg
+#else
+ #define dhcps_dbg(...) do {} while(0)
+#endif
/* default configurations */
#define DHCP_SERVER_OPENDNS long_be(0xd043dede) /* OpenDNS DNS server 208.67.222.222 */
@@ -68,7 +71,7 @@ static int dhcp_settings_cmp(void *ka, void *kb)
return (a->dev < b->dev) ? (-1) : (1);
}
-PICO_TREE_DECLARE(DHCPSettings, dhcp_settings_cmp);
+static PICO_TREE_DECLARE(DHCPSettings, dhcp_settings_cmp);
static int dhcp_negotiations_cmp(void *ka, void *kb)
{
@@ -78,7 +81,7 @@ static int dhcp_negotiations_cmp(void *ka, void *kb)
return (a->xid < b->xid) ? (-1) : (1);
}
-PICO_TREE_DECLARE(DHCPNegotiations, dhcp_negotiations_cmp);
+static PICO_TREE_DECLARE(DHCPNegotiations, dhcp_negotiations_cmp);
static inline void dhcps_set_default_pool_start_if_not_provided(struct pico_dhcp_server_setting *dhcps)
@@ -114,7 +117,12 @@ static inline struct pico_dhcp_server_setting *dhcps_try_open_socket(struct pico
return NULL;
}
- pico_tree_insert(&DHCPSettings, dhcps);
+ if (pico_tree_insert(&DHCPSettings, dhcps)) {
+ dhcps_dbg("DHCP server ERROR: could not insert settings in tree\n");
+ PICO_FREE(dhcps);
+ return NULL;
+ }
+
return dhcps;
}
@@ -220,7 +228,12 @@ static struct pico_dhcp_server_negotiation *pico_dhcp_server_add_negotiation(str
}
dhcp_negotiation_set_ciaddr(dhcpn);
- pico_tree_insert(&DHCPNegotiations, dhcpn);
+ if (pico_tree_insert(&DHCPNegotiations, dhcpn)) {
+ dhcps_dbg("DHCP server ERROR: could not insert negotiations in tree\n");
+ PICO_FREE(dhcpn);
+ return NULL;
+ }
+
return dhcpn;
}
@@ -256,14 +269,14 @@ static void dhcpd_make_reply(struct pico_dhcp_server_negotiation *dhcpn, uint8_t
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));
+ 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;
@@ -285,7 +298,7 @@ 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);
+ dhcps_dbg("DHCP server: message type %u\n", *msgtype);
}
}
@@ -324,7 +337,7 @@ static inline void dhcps_make_reply_to_discover_or_request(struct pico_dhcp_serv
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);
+ struct pico_dhcp_opt *opt = DHCP_OPT(hdr, 0);
uint8_t msgtype = 0;
struct pico_ip4 reqip = {
0
@@ -347,7 +360,7 @@ static void pico_dhcp_server_recv(struct pico_socket *s, uint8_t *buf, uint32_t
struct pico_dhcp_server_negotiation *dhcpn = NULL;
struct pico_device *dev = NULL;
- if (!pico_dhcp_are_options_valid(DHCP_OPT(hdr,0), optlen))
+ if (!pico_dhcp_are_options_valid(DHCP_OPT(hdr, 0), optlen))
return;
dev = pico_ipv4_link_find(&s->local_addr.ip4);
@@ -395,7 +408,9 @@ int pico_dhcp_server_initiate(struct pico_dhcp_server_setting *setting)
int pico_dhcp_server_destroy(struct pico_device *dev)
{
- struct pico_dhcp_server_setting *found, test = { 0 };
+ struct pico_dhcp_server_setting *found, test = {
+ 0
+ };
test.dev = dev;
found = pico_tree_findKey(&DHCPSettings, &test);
if (!found) {
diff --git a/ext/picotcp/modules/pico_dhcp_server.h b/ext/picotcp/modules/pico_dhcp_server.h
index d5df820..45a9b1b 100644
--- a/ext/picotcp/modules/pico_dhcp_server.h
+++ b/ext/picotcp/modules/pico_dhcp_server.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef INCLUDE_PICO_DHCP_SERVER
diff --git a/ext/picotcp/modules/pico_dns_client.c b/ext/picotcp/modules/pico_dns_client.c
index 806cff5..91d9cc3 100644
--- a/ext/picotcp/modules/pico_dns_client.c
+++ b/ext/picotcp/modules/pico_dns_client.c
@@ -1,6 +1,6 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- See LICENSE and COPYING for usage.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Authors: Kristof Roelants
*********************************************************************/
@@ -18,8 +18,11 @@
#ifdef PICO_SUPPORT_IPV4
-#define dns_dbg(...) do {} while(0)
-/* #define dns_dbg dbg */
+#ifdef DEBUG_DNS
+ #define dns_dbg dbg
+#else
+ #define dns_dbg(...) do {} while(0)
+#endif
/* DNS response length */
#define PICO_DNS_MAX_QUERY_LEN 255
@@ -43,7 +46,7 @@ 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);
+static PICO_TREE_DECLARE(NSTable, dns_ns_cmp);
struct pico_dns_query
{
@@ -67,7 +70,7 @@ static int dns_query_cmp(void *ka, void *kb)
return (a->id < b->id) ? (-1) : (1);
}
-PICO_TREE_DECLARE(DNSTable, dns_query_cmp);
+static PICO_TREE_DECLARE(DNSTable, dns_query_cmp);
static int pico_dns_client_del_ns(struct pico_ip4 *ns_addr)
{
@@ -91,13 +94,15 @@ static int pico_dns_client_del_ns(struct pico_ip4 *ns_addr)
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 */
+ 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;
+ return NULL;
}
dns = PICO_ZALLOC(sizeof(struct pico_dns_ns));
@@ -109,9 +114,12 @@ static struct pico_dns_ns *pico_dns_client_add_ns(struct pico_ip4 *ns_addr)
dns->ns = *ns_addr;
found = pico_tree_insert(&NSTable, dns);
- if (found) { /* nameserver already present */
+ if (found) { /* nameserver already present or out of memory */
PICO_FREE(dns);
- return found;
+ if ((void *)found == (void *)&LEAF)
+ return NULL;
+ else
+ return found;
}
/* default NS found, remove it */
@@ -167,7 +175,8 @@ static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_header *
found = pico_tree_insert(&DNSTable, q);
if (found) {
- pico_err = PICO_ERR_EAGAIN;
+ if ((void *)found != (void *)&LEAF) /* If found == &LEAF we're out of memory and pico_err is set */
+ pico_err = PICO_ERR_EAGAIN;
pico_socket_close(q->s);
PICO_FREE(q);
return NULL;
@@ -298,7 +307,7 @@ static int pico_dns_client_check_asuffix(struct pico_dns_record_suffix *suf, str
}
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);
+ dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", long_be(suf->rttl), PICO_DNS_MAX_TTL);
return -1;
}
@@ -369,7 +378,10 @@ static int pico_dns_client_send(struct pico_dns_query *q)
pico_socket_send(q->s, q->query, q->len);
*paramID = q->id;
- pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, paramID);
+ if (!pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, paramID)) {
+ dns_dbg("DNS: Failed to start retransmission timer\n");
+ goto failure;
+ }
return 0;
@@ -408,12 +420,38 @@ static void pico_dns_client_retransmission(pico_time now, void *arg)
}
}
+static int pico_dns_client_check_rdlength(uint16_t qtype, uint16_t rdlength)
+{
+ switch (qtype)
+ {
+ case PICO_DNS_TYPE_A:
+ if (rdlength != PICO_DNS_RR_A_RDLENGTH)
+ return -1;
+ break;
+#ifdef PICO_SUPPORT_IPV6
+ case PICO_DNS_TYPE_AAAA:
+ if (rdlength != PICO_DNS_RR_AAAA_RDLENGTH)
+ return -1;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int pico_dns_client_user_callback(struct pico_dns_record_suffix *asuffix, struct pico_dns_query *q)
{
uint32_t ip = 0;
char *str = NULL;
char *rdata = (char *) asuffix + sizeof(struct pico_dns_record_suffix);
+ if (pico_dns_client_check_rdlength(q->qtype, short_be(asuffix->rdlength)) < 0) {
+ dns_dbg("DNS ERROR: Invalid RR rdlength: %u\n", short_be(asuffix->rdlength));
+ return -1;
+ }
+
switch (q->qtype)
{
case PICO_DNS_TYPE_A:
diff --git a/ext/picotcp/modules/pico_dns_client.h b/ext/picotcp/modules/pico_dns_client.h
index 910cc92..da3f313 100644
--- a/ext/picotcp/modules/pico_dns_client.h
+++ b/ext/picotcp/modules/pico_dns_client.h
@@ -1,6 +1,6 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- See LICENSE and COPYING for usage.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -33,6 +33,10 @@
#define PICO_DNS_NS_DEFAULT "208.67.222.222"
#define PICO_DNS_NS_PORT 53
+/* RDLENGTH for A and AAAA RR's */
+#define PICO_DNS_RR_A_RDLENGTH 4
+#define PICO_DNS_RR_AAAA_RDLENGTH 16
+
int pico_dns_client_init(void);
/* flag is PICO_DNS_NS_DEL or PICO_DNS_NS_ADD */
int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag);
diff --git a/ext/picotcp/modules/pico_dns_common.c b/ext/picotcp/modules/pico_dns_common.c
index 0c0250e..44d89da 100644
--- a/ext/picotcp/modules/pico_dns_common.c
+++ b/ext/picotcp/modules/pico_dns_common.c
@@ -1,6 +1,6 @@
/* ****************************************************************************
* PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- * See LICENSE and COPYING for usage.
+ * See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
* .
* Authors: Toon Stegen, Jelle De Vleeschouwer
* ****************************************************************************/
@@ -13,14 +13,17 @@
#include "pico_dns_common.h"
#include "pico_tree.h"
-#define dns_dbg(...) do {} while(0)
-//#define dns_dbg dbg
+#ifdef DEBUG_DNS
+ #define dns_dbg dbg
+#else
+ #define dns_dbg(...) do {} while(0)
+#endif
/* MARK: v NAME & IP FUNCTIONS */
#define dns_name_foreach_label_safe(label, name, next, maxlen) \
- for ((label) = (name), (next) = (char *)((name) + *(name) + 1); \
+ for ((label) = (name), (next) = (char *)((name) + *(unsigned char*)(name) + 1); \
(*(label) != '\0') && ((uint16_t)((label) - (name)) < (maxlen)); \
- (label) = (next), (next) = (char *)((next) + *(next) + 1))
+ (label) = (next), (next) = (char *)((next) + *(unsigned char*)(next) + 1))
/* ****************************************************************************
* Checks if the DNS name doesn't exceed 256 bytes including zero-byte.
@@ -208,7 +211,7 @@ pico_dns_url_to_reverse_qname( const char *url, uint8_t proto )
return NULL;
}
- pico_dns_name_to_dns_notation(reverse_qname, (unsigned int)(slen + arpalen));
+ pico_dns_name_to_dns_notation(reverse_qname, (uint16_t)(slen + arpalen));
return reverse_qname;
}
@@ -305,13 +308,13 @@ pico_dns_strlen( const char *url )
* @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 )
+int pico_dns_name_to_dns_notation( char *url, uint16_t maxlen )
{
char c = '\0';
char *lbl = url, *i = url;
/* Check params */
- if (!url || pico_dns_check_namelen((uint16_t)maxlen)) {
+ if (!url || pico_dns_check_namelen(maxlen)) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
@@ -339,12 +342,12 @@ int pico_dns_name_to_dns_notation( char *url, unsigned int maxlen )
* @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 )
+int pico_dns_notation_to_name( char *ptr, uint16_t maxlen )
{
char *label = NULL, *next = NULL;
/* Iterate safely over the labels and update each label */
- dns_name_foreach_label_safe(label, ptr, next, (uint16_t)maxlen) {
+ dns_name_foreach_label_safe(label, ptr, next, maxlen) {
*label = '.';
}
@@ -585,8 +588,8 @@ pico_dns_question_fill_name( char **qname,
* 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
+ * @param reverse When this is true, a reverse resolution name will be
+ * generated from the URL
* @return Returns pointer to the created DNS Question on success, NULL on
* failure.
* ****************************************************************************/
@@ -875,6 +878,7 @@ pico_dns_record_fill_rdata( uint8_t **rdata,
if (datalen == 0) {
return datalen;
}
+
_datalen = datalen;
if (!(*rdata = (uint8_t *)PICO_ZALLOC((size_t)datalen))) {
pico_err = PICO_ERR_ENOMEM;
@@ -977,6 +981,7 @@ static int pico_tolower(int c)
{
if ((c >= 'A') && (c <= 'Z'))
c += 'a' - 'A';
+
return c;
}
@@ -990,8 +995,9 @@ static int pico_tolower(int c)
* @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
+ * @param caseinsensitive Whether or not the bytes are compared
+ * case-insensitive. Should be either
+ * PICO_DNS_CASE_SENSITIVE or PICO_DNS_CASE_INSENSITIVE
* @return 0 when the buffers are equal, returns difference when they're not.
* ****************************************************************************/
int
@@ -1006,6 +1012,7 @@ pico_dns_rdata_cmp( uint8_t *a, uint8_t *b,
if (!a || !b) {
if (!a && !b)
return 0;
+
pico_err = PICO_ERR_EINVAL;
return -1;
}
@@ -1016,7 +1023,7 @@ pico_dns_rdata_cmp( uint8_t *a, uint8_t *b,
slen = rdlength_b;
/* loop over slen */
- if(caseinsensitive){
+ if(caseinsensitive) {
for (i = 0; i < slen; i++) {
if ((dif = pico_tolower((int)a[i]) - pico_tolower((int)b[i]))) {
return dif;
@@ -1024,7 +1031,7 @@ pico_dns_rdata_cmp( uint8_t *a, uint8_t *b,
}
}else{
for (i = 0; i < slen; i++) {
- if ((dif = (int)a[i] - (int)b[i])){
+ if ((dif = (int)a[i] - (int)b[i])) {
return dif;
}
}
@@ -1065,7 +1072,7 @@ pico_dns_question_cmp( void *qa,
/* Then compare qnames */
return pico_dns_rdata_cmp((uint8_t *)a->qname, (uint8_t *)b->qname,
pico_dns_strlen(a->qname),
- pico_dns_strlen(b->qname), 1);
+ pico_dns_strlen(b->qname), PICO_DNS_CASE_INSENSITIVE);
}
/* ****************************************************************************
@@ -1100,7 +1107,7 @@ pico_dns_record_cmp_name_type( void *ra,
/* Then compare names */
return pico_dns_rdata_cmp((uint8_t *)(a->rname), (uint8_t *)(b->rname),
(uint16_t)strlen(a->rname),
- (uint16_t)strlen(b->rname), 1);
+ (uint16_t)strlen(b->rname), PICO_DNS_CASE_INSENSITIVE);
}
/* ****************************************************************************
@@ -1131,7 +1138,7 @@ pico_dns_record_cmp( void *ra,
/* Then compare rdata */
return pico_dns_rdata_cmp(a->rdata, b->rdata,
short_be(a->rsuffix->rdlength),
- short_be(b->rsuffix->rdlength), 0);
+ short_be(b->rsuffix->rdlength), PICO_DNS_CASE_SENSITIVE);
}
/* MARK: ^ COMPARING */
@@ -1534,8 +1541,9 @@ pico_dns_packet_compress_name( uint8_t *name,
/* Move up left over data */
difference = (uint16_t)(ptr_after_str - offset);
last_byte = packet + *len;
- for (i = ptr_after_str; i < last_byte; i++)
+ for (i = ptr_after_str; i < last_byte; i++) {
*((uint8_t *)(i - difference)) = *i;
+ }
/* Update length */
*len = (uint16_t)(*len - difference);
break;
@@ -1613,8 +1621,9 @@ pico_dns_packet_compress( pico_dns_packet *packet, uint16_t *len )
/* Start with the questions */
for (i = 0; i < qdcount; i++) {
- if (i) /* First question can't be compressed */
+ if(i) { /* First question can't be compressed */
pico_dns_packet_compress_name(iterator, packet_buf, len);
+ }
/* Move to next question */
iterator = (uint8_t *)(iterator +
@@ -1628,7 +1637,7 @@ pico_dns_packet_compress( pico_dns_packet *packet, uint16_t *len )
}
/* ****************************************************************************
- * Calculates how big a packet needs be in order to store all the question &
+ * Calculates how big a packet needs be in order to store all the questions &
* records in the tree. Also determines the amount of questions and records.
*
* @param qtree Tree with Questions.
@@ -1711,6 +1720,7 @@ pico_dns_packet_create( struct pico_tree *qtree,
if (qtree && pico_tree_count(&_qtree) != 0) {
if (pico_dns_fill_packet_question_section(packet, &_qtree)) {
dns_dbg("Could not fill Question Section correctly!\n");
+ PICO_FREE(packet);
return NULL;
}
}
@@ -1719,6 +1729,7 @@ pico_dns_packet_create( struct pico_tree *qtree,
if (pico_dns_fill_packet_rr_sections(packet, &_qtree, &_antree,
&_nstree, &_artree)) {
dns_dbg("Could not fill Resource Record Sections correctly!\n");
+ PICO_FREE(packet);
return NULL;
}
diff --git a/ext/picotcp/modules/pico_dns_common.h b/ext/picotcp/modules/pico_dns_common.h
index 32a1a1d..8272128 100644
--- a/ext/picotcp/modules/pico_dns_common.h
+++ b/ext/picotcp/modules/pico_dns_common.h
@@ -1,7 +1,7 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- See LICENSE and COPYING for usage.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Authors: Toon Stegen, Jelle De Vleeschouwer
*********************************************************************/
@@ -53,6 +53,10 @@
#define PICO_ARPA_IPV6_SUFFIX ".IP6.ARPA"
#endif
+/* Used in pico_dns_rdata_cmp */
+#define PICO_DNS_CASE_SENSITIVE 0x00u
+#define PICO_DNS_CASE_INSENSITIVE 0x01u
+
#define PICO_DNS_NAMEBUF_SIZE (256)
enum pico_dns_arpa
@@ -191,7 +195,7 @@ pico_dns_strlen( const char *url );
* @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 );
+int pico_dns_name_to_dns_notation( char *url, uint16_t maxlen );
/* ****************************************************************************
* Replaces the label lengths in a DNS-name by .'s. So it actually converts a
@@ -202,7 +206,7 @@ int pico_dns_name_to_dns_notation( char *url, unsigned int maxlen );
* @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 );
+int pico_dns_notation_to_name( char *ptr, uint16_t maxlen );
/* ****************************************************************************
* Determines the length of the first label of a DNS name in URL-format
@@ -362,8 +366,9 @@ pico_dns_record_decompress( struct pico_dns_record *record,
* @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
+ * @param caseinsensitive Whether or not the bytes are compared
+ * case-insensitive. Should be either
+ * PICO_DNS_CASE_SENSITIVE or PICO_DNS_CASE_INSENSITIVE
* @return 0 when the buffers are equal, returns difference when they're not.
* ****************************************************************************/
int
diff --git a/ext/picotcp/modules/pico_dns_sd.c b/ext/picotcp/modules/pico_dns_sd.c
index 1a60c2a..d845b73 100644
--- a/ext/picotcp/modules/pico_dns_sd.c
+++ b/ext/picotcp/modules/pico_dns_sd.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2014-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Author: Jelle De Vleeschouwer
*********************************************************************/
@@ -10,8 +10,11 @@
#ifdef PICO_SUPPORT_DNS_SD
/* --- Debugging --- */
-#define dns_sd_dbg(...) do {} while(0)
-//#define dns_sd_dbg dbg
+#ifdef DEBUG_DNS_SD
+ #define dns_sd_dbg dbg
+#else
+ #define dns_sd_dbg(...) do {} while(0)
+#endif
/* --- PROTOTYPES --- */
key_value_pair_t *
@@ -52,9 +55,10 @@ pico_dns_sd_kv_vector_strlen( kv_vector *vector )
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)
+ if (iterator->value) {
len = (uint16_t) (len + 1u /* '=' char */ +
strlen(iterator->value) /* Length of value */);
+ }
}
return len;
}
@@ -147,9 +151,10 @@ pico_dns_sd_txt_record_create( const char *url,
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)
+ if (len <= 1) {
return NULL;
-
+ }
+
/* Provide space for the txt buf */
if (!(txt = (char *)PICO_ZALLOC(len))) {
pico_err = PICO_ERR_ENOMEM;
@@ -377,7 +382,7 @@ pico_dns_sd_init( const char *_hostname,
}
/* ****************************************************************************
- * Just calls pico_mdns_init in it's turn to initialise the mDNS-module.
+ * Just calls pico_mdns_init in its turn to initialise the mDNS-module.
* See pico_mdns.h for description.
* ****************************************************************************/
int
@@ -399,8 +404,10 @@ pico_dns_sd_register_service( const char *name,
/* 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)
+ if (url) {
PICO_FREE(url);
+ }
+
pico_err = PICO_ERR_EINVAL;
return -1;
}
@@ -421,15 +428,27 @@ pico_dns_sd_register_service( const char *name,
/* 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);
+ if (txt_record) {
+ if (pico_tree_insert(&rtree, txt_record) == &LEAF) {
+ PICO_MDNS_RTREE_DESTROY(&rtree);
+ pico_mdns_record_delete((void **)&txt_record);
+ pico_mdns_record_delete((void **)&srv_record);
+ return -1;
+ }
+ }
- pico_tree_insert(&rtree, srv_record);
+ if (pico_tree_insert(&rtree, srv_record) == &LEAF) {
+ PICO_MDNS_RTREE_DESTROY(&rtree);
+ pico_mdns_record_delete((void **)&srv_record);
+ return -1;
+ }
if (pico_mdns_claim(rtree, callback, arg)) {
PICO_MDNS_RTREE_DESTROY(&rtree);
return -1;
}
+
+ /* Only destroy the tree, not its elements since they still exist in another tree */
pico_tree_destroy(&rtree, NULL);
return 0;
}
diff --git a/ext/picotcp/modules/pico_dns_sd.h b/ext/picotcp/modules/pico_dns_sd.h
index b93d599..ec9f727 100644
--- a/ext/picotcp/modules/pico_dns_sd.h
+++ b/ext/picotcp/modules/pico_dns_sd.h
@@ -1,6 +1,6 @@
/* ****************************************************************************
* PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved.
- * See LICENSE and COPYING for usage.
+ * See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
* .
* Author: Jelle De Vleeschouwer
* ****************************************************************************/
diff --git a/ext/picotcp/modules/pico_ethernet.c b/ext/picotcp/modules/pico_ethernet.c
new file mode 100644
index 0000000..cc86e85
--- /dev/null
+++ b/ext/picotcp/modules/pico_ethernet.c
@@ -0,0 +1,447 @@
+ /*********************************************************************
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
+
+ .
+
+ Authors: Daniele Lacamera
+ *********************************************************************/
+
+#include "pico_config.h"
+#include "pico_stack.h"
+#include "pico_ipv4.h"
+#include "pico_ipv6.h"
+#include "pico_icmp4.h"
+#include "pico_icmp6.h"
+#include "pico_arp.h"
+#include "pico_ethernet.h"
+
+#define IS_LIMITED_BCAST(f) (((struct pico_ipv4_hdr *) f->net_hdr)->dst.addr == PICO_IP4_BCAST)
+
+#ifdef PICO_SUPPORT_ETH
+
+const uint8_t PICO_ETHADDR_ALL[6] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+# define PICO_SIZE_MCAST 3
+static const uint8_t PICO_ETHADDR_MCAST[6] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
+};
+
+#ifdef PICO_SUPPORT_IPV6
+# define PICO_SIZE_MCAST6 2
+static const uint8_t PICO_ETHADDR_MCAST6[6] = {
+ 0x33, 0x33, 0x00, 0x00, 0x00, 0x00
+};
+#endif
+
+/* DATALINK LEVEL: interface from network to the device
+ * and vice versa.
+ */
+
+/* The pico_ethernet_receive() function is used by
+ * those devices supporting ETH in order to push packets up
+ * into the stack.
+ */
+
+/* Queues */
+static struct pico_queue ethernet_in = {
+ 0
+};
+static struct pico_queue ethernet_out = {
+ 0
+};
+
+int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f);
+static int32_t pico_ethernet_receive(struct pico_frame *f);
+
+static int pico_ethernet_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+ IGNORE_PARAMETER(self);
+ return pico_ethernet_send(f);
+}
+
+static int pico_ethernet_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
+ IGNORE_PARAMETER(self);
+ return (pico_ethernet_receive(f) <= 0); /* 0 on success, which is ret > 0 */
+}
+
+static struct pico_frame *pico_ethernet_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size)
+{
+ struct pico_frame *f = NULL;
+ uint32_t overhead = 0;
+ IGNORE_PARAMETER(self);
+
+ if (dev)
+ overhead = dev->overhead;
+
+ f = pico_frame_alloc((uint32_t)(overhead + size + PICO_SIZE_ETHHDR));
+ if (!f)
+ return NULL;
+
+ f->dev = dev;
+ f->datalink_hdr = f->buffer + overhead;
+ f->net_hdr = f->datalink_hdr + PICO_SIZE_ETHHDR;
+ /* Stay of the rest, higher levels will take care */
+
+ return f;
+}
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_ethernet = {
+ .name = "ethernet",
+ .layer = PICO_LAYER_DATALINK,
+ .alloc = pico_ethernet_alloc,
+ .process_in = pico_ethernet_process_in,
+ .process_out = pico_ethernet_process_out,
+ .q_in = ðernet_in,
+ .q_out = ðernet_out,
+};
+
+static int destination_is_bcast(struct pico_frame *f)
+{
+ if (!f)
+ return 0;
+
+ if (IS_IPV6(f))
+ return 0;
+
+#ifdef PICO_SUPPORT_IPV4
+ else {
+ struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+ return pico_ipv4_is_broadcast(hdr->dst.addr);
+ }
+#else
+ return 0;
+#endif
+}
+
+static int destination_is_mcast(struct pico_frame *f)
+{
+ int ret = 0;
+ if (!f)
+ return 0;
+
+#ifdef PICO_SUPPORT_IPV6
+ if (IS_IPV6(f)) {
+ struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *) f->net_hdr;
+ ret = pico_ipv6_is_multicast(hdr->dst.addr);
+ }
+
+#endif
+#ifdef PICO_SUPPORT_IPV4
+ else {
+ struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+ ret = pico_ipv4_is_multicast(hdr->dst.addr);
+ }
+#endif
+
+ return ret;
+}
+
+#ifdef PICO_SUPPORT_IPV4
+static int32_t pico_ipv4_ethernet_receive(struct pico_frame *f)
+{
+ if (IS_IPV4(f)) {
+ if (pico_enqueue(pico_proto_ipv4.q_in, f) < 0) {
+ pico_frame_discard(f);
+ return -1;
+ }
+ } else {
+ (void)pico_icmp4_param_problem(f, 0);
+ pico_frame_discard(f);
+ return -1;
+ }
+
+ return (int32_t)f->buffer_len;
+}
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+static int32_t pico_ipv6_ethernet_receive(struct pico_frame *f)
+{
+ if (IS_IPV6(f)) {
+ if (pico_enqueue(pico_proto_ipv6.q_in, f) < 0) {
+ pico_frame_discard(f);
+ return -1;
+ }
+ } else {
+ /* Wrong version for link layer type */
+ pico_frame_discard(f);
+ return -1;
+ }
+
+ return (int32_t)f->buffer_len;
+}
+#endif
+
+static int32_t pico_eth_receive(struct pico_frame *f)
+{
+ struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+ f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr);
+
+#if (defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH)
+ if (hdr->proto == PICO_IDETH_ARP)
+ return pico_arp_receive(f);
+#endif
+
+#if defined (PICO_SUPPORT_IPV4)
+ if (hdr->proto == PICO_IDETH_IPV4)
+ return pico_ipv4_ethernet_receive(f);
+#endif
+
+#if defined (PICO_SUPPORT_IPV6)
+ if (hdr->proto == PICO_IDETH_IPV6)
+ return pico_ipv6_ethernet_receive(f);
+#endif
+
+ pico_frame_discard(f);
+ return -1;
+}
+
+static void pico_eth_check_bcast(struct pico_frame *f)
+{
+ struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+ /* Indicate a link layer broadcast packet */
+ if (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) == 0)
+ f->flags |= PICO_FRAME_FLAG_BCAST;
+}
+
+static int32_t pico_ethernet_receive(struct pico_frame *f)
+{
+ struct pico_eth_hdr *hdr;
+ if (!f || !f->dev || !f->datalink_hdr)
+ {
+ pico_frame_discard(f);
+ return -1;
+ }
+
+ hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+ if ((memcmp(hdr->daddr, f->dev->eth->mac.addr, PICO_SIZE_ETH) != 0) &&
+ (memcmp(hdr->daddr, PICO_ETHADDR_MCAST, PICO_SIZE_MCAST) != 0) &&
+#ifdef PICO_SUPPORT_IPV6
+ (memcmp(hdr->daddr, PICO_ETHADDR_MCAST6, PICO_SIZE_MCAST6) != 0) &&
+#endif
+ (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) != 0))
+ {
+ pico_frame_discard(f);
+ return -1;
+ }
+
+ pico_eth_check_bcast(f);
+ return pico_eth_receive(f);
+}
+
+static struct pico_eth *pico_ethernet_mcast_translate(struct pico_frame *f, uint8_t *pico_mcast_mac)
+{
+ struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+
+ /* place 23 lower bits of IP in lower 23 bits of MAC */
+ pico_mcast_mac[5] = (long_be(hdr->dst.addr) & 0x000000FFu);
+ pico_mcast_mac[4] = (uint8_t)((long_be(hdr->dst.addr) & 0x0000FF00u) >> 8u);
+ pico_mcast_mac[3] = (uint8_t)((long_be(hdr->dst.addr) & 0x007F0000u) >> 16u);
+
+ return (struct pico_eth *)pico_mcast_mac;
+}
+
+
+#ifdef PICO_SUPPORT_IPV6
+static struct pico_eth *pico_ethernet_mcast6_translate(struct pico_frame *f, uint8_t *pico_mcast6_mac)
+{
+ struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
+
+ /* first 2 octets are 0x33, last four are the last four of dst */
+ pico_mcast6_mac[5] = hdr->dst.addr[PICO_SIZE_IP6 - 1];
+ pico_mcast6_mac[4] = hdr->dst.addr[PICO_SIZE_IP6 - 2];
+ pico_mcast6_mac[3] = hdr->dst.addr[PICO_SIZE_IP6 - 3];
+ pico_mcast6_mac[2] = hdr->dst.addr[PICO_SIZE_IP6 - 4];
+
+ return (struct pico_eth *)pico_mcast6_mac;
+}
+#endif
+
+static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const dstmac)
+{
+ int retval = -1;
+ if (!dstmac)
+ return -1;
+
+ #ifdef PICO_SUPPORT_IPV6
+ if (destination_is_mcast(f)) {
+ uint8_t pico_mcast6_mac[6] = {
+ 0x33, 0x33, 0x00, 0x00, 0x00, 0x00
+ };
+ pico_ethernet_mcast6_translate(f, pico_mcast6_mac);
+ memcpy(dstmac, pico_mcast6_mac, PICO_SIZE_ETH);
+ retval = 0;
+ } else {
+ struct pico_eth *neighbor = pico_ipv6_get_neighbor(f);
+ if (neighbor)
+ {
+ memcpy(dstmac, neighbor, PICO_SIZE_ETH);
+ retval = 0;
+ }
+ }
+
+ #else
+ (void)f;
+ pico_err = PICO_ERR_EPROTONOSUPPORT;
+ #endif
+ return retval;
+}
+
+
+/* Ethernet send, first attempt: try our own address.
+ * Returns 0 if the packet is not for us.
+ * Returns 1 if the packet is cloned to our own receive queue, so the caller can discard the original frame.
+ * */
+static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ /* Check own mac */
+ if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) {
+ struct pico_frame *clone = pico_frame_copy(f);
+ dbg("sending out packet destined for our own mac\n");
+ if (pico_ethernet_receive(clone) < 0) {
+ dbg("pico_ethernet_receive() failed\n");
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Ethernet send, second attempt: try bcast.
+ * Returns 0 if the packet is not bcast, so it will be handled somewhere else.
+ * Returns 1 if the packet is handled by the pico_device_broadcast() function, so it can be discarded.
+ * */
+static int32_t pico_ethsend_bcast(struct pico_frame *f)
+{
+ if (IS_LIMITED_BCAST(f)) {
+ (void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Ethernet send, third attempt: try unicast.
+ * If the device driver is busy, we return 0, so the stack won't discard the frame.
+ * In case of success, we can safely return 1.
+ */
+static int32_t pico_ethsend_dispatch(struct pico_frame *f)
+{
+ return (pico_sendto_dev(f) > 0); // Return 1 on success, ret > 0
+}
+
+/* Checks whether or not there's enough headroom allocated in the frame to
+ * prepend the Ethernet header. Reallocates if this is not the case. */
+static int eth_check_headroom(struct pico_frame *f)
+{
+ uint32_t headroom = (uint32_t)(f->net_hdr - f->buffer);
+ uint32_t grow = (uint32_t)(PICO_SIZE_ETHHDR - headroom);
+ if (headroom < (uint32_t)PICO_SIZE_ETHHDR) {
+ return pico_frame_grow_head(f, (uint32_t)(f->buffer_len + grow));
+ }
+ return 0;
+}
+
+/* This function looks for the destination mac address
+ * in order to send the frame being processed.
+ */
+int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f)
+{
+ struct pico_eth dstmac;
+ uint8_t dstmac_valid = 0;
+ uint16_t proto = PICO_IDETH_IPV4;
+
+#ifdef PICO_SUPPORT_IPV6
+ /* Step 1: If the frame has an IPv6 packet,
+ * destination address is taken from the ND tables
+ */
+ if (IS_IPV6(f)) {
+ if (pico_ethernet_ipv6_dst(f, &dstmac) < 0)
+ {
+ /* Enqueue copy of frame in IPv6 ND-module to retry later. Discard
+ * frame, otherwise we have a duplicate in IPv6-ND */
+ pico_ipv6_nd_postpone(f);
+ return (int32_t)f->len;
+ }
+
+ dstmac_valid = 1;
+ proto = PICO_IDETH_IPV6;
+ }
+ else
+#endif
+
+ /* In case of broadcast (IPV4 only), dst mac is FF:FF:... */
+ if (IS_BCAST(f) || destination_is_bcast(f))
+ {
+ memcpy(&dstmac, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
+ dstmac_valid = 1;
+ }
+
+ /* In case of multicast, dst mac is translated from the group address */
+ else if (destination_is_mcast(f)) {
+ uint8_t pico_mcast_mac[6] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
+ };
+ pico_ethernet_mcast_translate(f, pico_mcast_mac);
+ memcpy(&dstmac, pico_mcast_mac, PICO_SIZE_ETH);
+ dstmac_valid = 1;
+ }
+
+#if (defined PICO_SUPPORT_IPV4)
+ else {
+ struct pico_eth *arp_get;
+ arp_get = pico_arp_get(f);
+ if (arp_get) {
+ memcpy(&dstmac, arp_get, PICO_SIZE_ETH);
+ dstmac_valid = 1;
+ } else {
+ /* Enqueue copy of frame in ARP-module to retry later. Discard
+ * frame otherwise we have a duplicate */
+ pico_arp_postpone(f);
+ return (int32_t)f->len;
+ }
+ }
+#endif
+
+ /* This sets destination and source address, then pushes the packet to the device. */
+ if (dstmac_valid) {
+ struct pico_eth_hdr *hdr;
+ if (!eth_check_headroom(f)) {
+ hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+ if ((f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR))
+ {
+ f->start -= PICO_SIZE_ETHHDR;
+ f->len += PICO_SIZE_ETHHDR;
+ f->datalink_hdr = f->start;
+ hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+ memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
+ memcpy(hdr->daddr, &dstmac, PICO_SIZE_ETH);
+ hdr->proto = proto;
+ }
+
+ if (pico_ethsend_local(f, hdr) || pico_ethsend_bcast(f) || pico_ethsend_dispatch(f)) {
+ /* one of the above functions has delivered the frame accordingly.
+ * (returned != 0). It is safe to directly return successfully.
+ * Lower level queue has frame, so don't discard */
+ return (int32_t)f->len;
+ }
+ }
+ }
+
+ /* Failure, frame could not be be enqueued in lower-level layer, safe
+ * to discard since something clearly went wrong */
+ pico_frame_discard(f);
+ return 0;
+}
+
+#endif /* PICO_SUPPORT_ETH */
+
+
diff --git a/ext/picotcp/modules/pico_ethernet.h b/ext/picotcp/modules/pico_ethernet.h
new file mode 100644
index 0000000..e60c467
--- /dev/null
+++ b/ext/picotcp/modules/pico_ethernet.h
@@ -0,0 +1,18 @@
+/*********************************************************************
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
+
+ .
+
+ Authors: Daniele Lacamera
+ *********************************************************************/
+
+#ifndef INCLUDE_PICO_ETHERNET
+#define INCLUDE_PICO_ETHERNET
+
+#include "pico_config.h"
+#include "pico_frame.h"
+
+extern struct pico_protocol pico_proto_ethernet;
+
+#endif /* INCLUDE_PICO_ETHERNET */
diff --git a/ext/picotcp/modules/pico_fragments.c b/ext/picotcp/modules/pico_fragments.c
index 33e69ca..81b92ed 100644
--- a/ext/picotcp/modules/pico_fragments.c
+++ b/ext/picotcp/modules/pico_fragments.c
@@ -1,6 +1,6 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- See LICENSE and COPYING for usage.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Laurens Miers, Daniele Lacamera
*********************************************************************/
@@ -25,13 +25,18 @@
#include "pico_constants.h"
#include "pico_fragments.h"
-#define frag_dbg(...) do {} while(0)
+#ifdef DEBUG_FRAG
+ #define frag_dbg dbg
+#else
+ #define frag_dbg(...) do {} while(0)
+#endif
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
#define IP6_FRAG_OFF(x) ((x & 0xFFF8u))
#define IP6_FRAG_MORE(x) ((x & 0x0001))
-#define IP6_FRAG_ID(x) ((uint32_t)((x->ext.frag.id[0] << 24) + (x->ext.frag.id[1] << 16) + \
- (x->ext.frag.id[2] << 8) + x->ext.frag.id[3]))
+#define IP6_FRAG_ID(x) ((uint32_t)(((uint32_t)x->ext.frag.id[0] << 24) + ((uint32_t)x->ext.frag.id[1] << 16) + \
+ ((uint32_t)x->ext.frag.id[2] << 8) + (uint32_t)x->ext.frag.id[3]))
+
#else
#define IP6_FRAG_OFF(x) (0)
#define IP6_FRAG_MORE(x) (0)
@@ -48,19 +53,22 @@
#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);
+static int pico_fragments_check_complete(struct pico_tree *tree, uint8_t proto, uint8_t net);
+static int pico_fragments_reassemble(struct pico_tree *tree, unsigned int len, uint8_t proto, uint8_t net);
+static int pico_fragments_get_more_flag(struct pico_frame *frame, uint8_t net);
+static uint32_t pico_fragments_get_offset(struct pico_frame *frame, uint8_t net);
+static void pico_fragments_send_notify(struct pico_frame *first);
+static uint16_t pico_fragments_get_header_length(uint8_t net);
+static void pico_fragments_empty_tree(struct pico_tree *tree);
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
static uint32_t ipv6_cur_frag_id = 0u;
-uint32_t ipv6_fragments_timer = 0u;
+static uint32_t ipv6_fragments_timer = 0u;
static int pico_ipv6_frag_compare(void *ka, void *kb)
{
@@ -73,35 +81,12 @@ static int pico_ipv6_frag_compare(void *ka, void *kb)
return 0;
}
-PICO_TREE_DECLARE(ipv6_fragments, pico_ipv6_frag_compare);
+static 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);
- }
+ if (pico_fragments_reassemble(&ipv6_fragments, len, proto, PICO_PROTO_IPV6) == 0)
+ {
pico_timer_cancel(ipv6_fragments_timer);
ipv6_fragments_timer = 0;
}
@@ -110,32 +95,36 @@ static void pico_ipv6_fragments_complete(unsigned int len, uint8_t proto)
static void pico_ipv6_frag_timer_on(void)
{
ipv6_fragments_timer = pico_timer_add(PICO_IPV6_FRAG_TIMEOUT, pico_frag_expire, &ipv6_fragments);
+ if (!ipv6_fragments_timer) {
+ frag_dbg("FRAG: Failed to start IPv6 expiration timer\n");
+ pico_fragments_empty_tree(&ipv6_fragments);
+ }
}
static int pico_ipv6_frag_match(struct pico_frame *a, struct pico_frame *b)
{
- struct pico_ipv6_hdr *ha, *hb;
+ struct pico_ipv6_hdr *ha = NULL, *hb = NULL;
if (!a || !b)
- return 0;
+ return -1;
ha = (struct pico_ipv6_hdr *)a->net_hdr;
hb = (struct pico_ipv6_hdr *)b->net_hdr;
if (!ha || !hb)
- return 0;
+ return -2;
if (memcmp(ha->src.addr, hb->src.addr, PICO_SIZE_IP6) != 0)
- return 0;
+ return 1;
if (memcmp(ha->dst.addr, hb->dst.addr, PICO_SIZE_IP6) != 0)
- return 0;
+ return 2;
- return 1;
+ return 0;
}
#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 uint32_t ipv4_fragments_timer = 0u;
static int pico_ipv4_frag_compare(void *ka, void *kb)
{
@@ -148,36 +137,12 @@ static int pico_ipv4_frag_compare(void *ka, void *kb)
return 0;
}
-PICO_TREE_DECLARE(ipv4_fragments, pico_ipv4_frag_compare);
+static 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);
- }
+ if (pico_fragments_reassemble(&ipv4_fragments, len, proto, PICO_PROTO_IPV4) == 0)
+ {
pico_timer_cancel(ipv4_fragments_timer);
ipv4_fragments_timer = 0;
}
@@ -186,26 +151,30 @@ static void pico_ipv4_fragments_complete(unsigned int len, uint8_t proto)
static void pico_ipv4_frag_timer_on(void)
{
ipv4_fragments_timer = pico_timer_add( PICO_IPV4_FRAG_TIMEOUT, pico_frag_expire, &ipv4_fragments);
+ if (!ipv4_fragments_timer) {
+ frag_dbg("FRAG: Failed to start IPv4 expiration timer\n");
+ pico_fragments_empty_tree(&ipv4_fragments);
+ }
}
static int pico_ipv4_frag_match(struct pico_frame *a, struct pico_frame *b)
{
struct pico_ipv4_hdr *ha, *hb;
if (!a || !b)
- return 0;
+ return -1;
ha = (struct pico_ipv4_hdr *)a->net_hdr;
hb = (struct pico_ipv4_hdr *)b->net_hdr;
if (!ha || !hb)
- return 0;
+ return -2;
if (memcmp(&(ha->src.addr), &(hb->src.addr), PICO_SIZE_IP4) != 0)
- return 0;
+ return 1;
if (memcmp(&(ha->dst.addr), &(hb->dst.addr), PICO_SIZE_IP4) != 0)
- return 0;
+ return 2;
- return 1;
+ return 0;
}
#endif
@@ -227,48 +196,53 @@ static void pico_fragments_complete(unsigned int bookmark, uint8_t proto, uint8_
#endif
}
-static int pico_fragments_check_complete(uint8_t proto, uint8_t net)
+static void pico_fragments_empty_tree(struct pico_tree *tree)
+{
+ struct pico_tree_node *index, *tmp;
+
+ if (!tree)
+ {
+ return;
+ }
+
+ pico_tree_foreach_safe(index, tree, tmp) {
+ struct pico_frame * old = index->keyValue;
+ pico_tree_delete(tree, old);
+ pico_frame_discard(old);
+ }
+
+}
+
+static int pico_fragments_check_complete(struct pico_tree *tree, uint8_t proto, uint8_t net)
{
struct pico_tree_node *index, *temp;
struct pico_frame *cur;
unsigned int bookmark = 0;
- 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
+ if (!tree)
+ return 0;
pico_tree_foreach_safe(index, tree, temp) {
cur = index->keyValue;
- if (FRAG_OFF(net, cur->frag) != bookmark)
- return 0;
+ if (cur) {
+ if (pico_fragments_get_offset(cur, net) != bookmark)
+ return -1;
- bookmark += cur->transport_len;
- if (!FRAG_MORE(net, cur->frag)) {
- pico_fragments_complete(bookmark, proto, net);
- return 1;
+ bookmark += cur->transport_len;
+ if (!pico_fragments_get_more_flag(cur, net)) {
+ pico_fragments_complete(bookmark, proto, net);
+ return 0;
+ }
}
}
- return 0;
+ return 1;
}
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;
+ IGNORE_PARAMETER(now);
if (!tree)
{
@@ -279,68 +253,243 @@ static void pico_frag_expire(pico_time now, void *arg)
first = pico_tree_first(tree);
if (!first) {
- frag_dbg("not first - not sending notify\n");
+ frag_dbg("Empty tree - not sending notify\n");
return;
}
+ pico_fragments_send_notify(first);
+
+ pico_fragments_empty_tree(tree);
+}
+
+static void pico_fragments_send_notify(struct pico_frame *first)
+{
+ uint8_t net = 0;
+
+ if (!first)
+ {
+ return;
+ }
+
+ if (0) {}
+
#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
- if (IS_IPV4(first))
+ else if (IS_IPV4(first))
{
net = PICO_PROTO_IPV4;
frag_dbg("Packet expired! ID:%hu\n", ipv4_cur_frag_id);
}
+
#endif
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
- if (IS_IPV6(first))
+ else 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))))
+ if (((pico_fragments_get_offset(first, net) == 0) && (pico_frame_dst_is_unicast(first))))
{
frag_dbg("sending notify\n");
pico_notify_frag_expired(first);
}
+ else
+ {
+ frag_dbg("Not first packet or not unicast address, not sending notify");
+ }
+}
- if (f)
- pico_tree_delete(tree, f);
+static int pico_fragments_reassemble(struct pico_tree *tree, unsigned int len, uint8_t proto, uint8_t net)
+{
+ struct pico_tree_node *index, *tmp;
+ struct pico_frame *f;
+ uint16_t header_length = 0;
+ unsigned int bookmark = 0;
+ struct pico_frame *full = NULL;
+ struct pico_frame *first = NULL;
- pico_frame_discard(first);
+ if (!tree)
+ {
+ frag_dbg("Cannot reassemble packet, no tree supplied!\n");
+ return -1;
+ }
+
+ first = pico_tree_first(tree);
+
+ if (!first)
+ {
+ frag_dbg("Cannot reassemble packet, empty tree supplied!\n");
+ return -2;
+ }
+
+ header_length = pico_fragments_get_header_length(net);
+
+ if (!header_length)
+ {
+ return -3;
+ }
+
+ full = pico_frame_alloc((uint16_t)(header_length + len));
+ if (full) {
+ full->net_hdr = full->buffer;
+ full->net_len = header_length;
+ memcpy(full->net_hdr, first->net_hdr, full->net_len);
+ full->transport_hdr = full->net_hdr + full->net_len;
+ full->transport_len = (uint16_t)len;
+ full->dev = first->dev;
+ pico_tree_foreach_safe(index, tree, tmp) {
+ f = index->keyValue;
+ memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len);
+ bookmark += f->transport_len;
+ pico_tree_delete(tree, f);
+ pico_frame_discard(f);
+ }
+ if (pico_transport_receive(full, proto) == -1)
+ {
+ pico_frame_discard(full);
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static uint16_t pico_fragments_get_header_length(uint8_t net)
+{
+ if (0) {}
+
+#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
+ else if (net == PICO_PROTO_IPV4)
+ {
+ return PICO_SIZE_IP4HDR;
+ }
+#endif
+#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
+ else if (net == PICO_PROTO_IPV6)
+ {
+ return PICO_SIZE_IP6HDR;
+ }
+#endif
+
+ return 0;
+}
+
+static int pico_fragments_get_more_flag(struct pico_frame *frame, uint8_t net)
+{
+ if (!frame)
+ {
+ frag_dbg("no frame given to determine more flag\n");
+ return 0;
+ }
+
+ if (0) {}
+
+#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
+ else if (net == PICO_PROTO_IPV4)
+ {
+ return IP4_FRAG_MORE(frame->frag);
+ }
+#endif
+#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
+ else if (net == PICO_PROTO_IPV6)
+ {
+ return IP6_FRAG_MORE(frame->frag);
+ }
+#endif
+
+ return 0;
+}
+
+static uint32_t pico_fragments_get_offset(struct pico_frame *frame, uint8_t net)
+{
+ if (!frame)
+ {
+ frag_dbg("no frame given to determine offset\n");
+ return 0;
+ }
+
+ if (0) {}
+
+#if defined(PICO_SUPPORT_IPV4) && defined(PICO_SUPPORT_IPV4FRAG)
+ else if (net == PICO_PROTO_IPV4)
+ {
+ return IP4_FRAG_OFF(frame->frag);
+ }
+#endif
+#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
+ else if (net == PICO_PROTO_IPV6)
+ {
+ return IP6_FRAG_OFF(frame->frag);
+ }
+#endif
+
+ return 0;
}
void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto)
{
#if defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_IPV6FRAG)
- struct pico_frame *first = pico_tree_first(&ipv6_fragments);
+ struct pico_frame *first = NULL;
+
+ if (!f || !frag)
+ {
+ frag_dbg("Bad arguments provided to pico_ipv6_process_frag\n");
+ return;
+ }
+
+ first = pico_tree_first(&ipv6_fragments);
+
+ if (first)
+ {
+ if ((pico_ipv6_frag_match(f, first) == 0 && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id))) {
+ struct pico_frame *temp = NULL;
+
+ temp = pico_frame_copy(f);
+
+ if (!temp) {
+ frag_dbg("Could not allocate memory to continue reassembly of IPV6 fragmented packet (id: %hu)\n", ipv6_cur_frag_id);
+ return;
+ }
+
+ if (pico_tree_insert(&ipv6_fragments, temp)) {
+ frag_dbg("FRAG: Could not insert picoframe in tree\n");
+ pico_frame_discard(temp);
+ return;
+ }
+ }
+ }
+ else
+ {
+ struct pico_frame *temp = NULL;
- if (!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;
}
+ temp = pico_frame_copy(f);
+
+ if (!temp) {
+ frag_dbg("Could not allocate memory to start reassembly of fragmented packet\n");
+ return;
+ }
+
pico_ipv6_frag_timer_on();
ipv6_cur_frag_id = IP6_FRAG_ID(frag);
frag_dbg("Started new reassembly, ID:%hu\n", ipv6_cur_frag_id);
+
+ if (pico_tree_insert(&ipv6_fragments, temp)) {
+ frag_dbg("FRAG: Could not insert picoframe in tree\n");
+ pico_frame_discard(temp);
+ return;
+ }
}
- 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);
+ pico_fragments_check_complete(&ipv6_fragments, proto, PICO_PROTO_IPV6);
#else
IGNORE_PARAMETER(frag);
IGNORE_PARAMETER(f);
@@ -351,38 +500,71 @@ void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f,
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);
+ struct pico_frame *first = NULL;
- /* 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))
+ if (!f || !hdr)
{
- /* 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;
+ frag_dbg("Bad arguments provided to pico_ipv4_process_frag\n");
+ return;
}
- f->frag = short_be(hdr->frag);
- if (!first) {
+ first = pico_tree_first(&ipv4_fragments);
+
+ if (first)
+ {
+ /* fragments from old packets still in tree, and new first fragment ? */
+ if ((IP4_FRAG_ID(hdr) != ipv4_cur_frag_id) && (IP4_FRAG_OFF(f->frag) == 0)) {
+ pico_fragments_empty_tree(&ipv4_fragments);
+
+ first = NULL;
+ ipv4_cur_frag_id = 0;
+ }
+
+ if ((pico_ipv4_frag_match(f, first) == 0 && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id))) {
+ struct pico_frame *temp = NULL;
+
+ temp = pico_frame_copy(f);
+
+ if (!temp) {
+ frag_dbg("Could not allocate memory to continue reassembly of IPV4 fragmented packet (id: %hu)\n", ipv4_cur_frag_id);
+ return;
+ }
+
+ if (pico_tree_insert(&ipv4_fragments, temp)) {
+ frag_dbg("FRAG: Could not insert picoframe in tree\n");
+ pico_frame_discard(temp);
+ return;
+ }
+ }
+ }
+ else
+ {
+ struct pico_frame *temp = NULL;
+
if (ipv4_cur_frag_id && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id)) {
/* Discard late arrivals, without firing the timer */
return;
}
+ temp = pico_frame_copy(f);
+
+ if (!temp) {
+ frag_dbg("Could not allocate memory to start reassembly fragmented packet\n");
+ return;
+ }
+
pico_ipv4_frag_timer_on();
ipv4_cur_frag_id = IP4_FRAG_ID(hdr);
+ frag_dbg("Started new reassembly, ID:%hu\n", ipv4_cur_frag_id);
+
+ if (pico_tree_insert(&ipv4_fragments, temp)) {
+ frag_dbg("FRAG: Could not insert picoframe in tree\n");
+ pico_frame_discard(temp);
+ return;
+ }
}
- 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);
+ pico_fragments_check_complete(&ipv4_fragments, proto, PICO_PROTO_IPV4);
#else
IGNORE_PARAMETER(hdr);
IGNORE_PARAMETER(f);
diff --git a/ext/picotcp/modules/pico_hotplug_detection.c b/ext/picotcp/modules/pico_hotplug_detection.c
index 732b4d7..0c362cb 100644
--- a/ext/picotcp/modules/pico_hotplug_detection.c
+++ b/ext/picotcp/modules/pico_hotplug_detection.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Frederik Van Slycken
*********************************************************************/
@@ -9,13 +9,14 @@
#include "pico_tree.h"
#include "pico_device.h"
-struct pico_hotplug_device{
- struct pico_device *dev;
- int prev_state;
- struct pico_tree callbacks;
+struct pico_hotplug_device {
+ struct pico_device *dev;
+ int prev_state;
+ struct pico_tree callbacks;
+ struct pico_tree init_callbacks; /* functions we still need to call for initialization */
};
-uint32_t timer_id = 0;
+static uint32_t timer_id = 0;
static int pico_hotplug_dev_cmp(void *ka, void *kb)
{
@@ -33,76 +34,144 @@ 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 PICO_TREE_DECLARE(Hotplug_device_tree, pico_hotplug_dev_cmp);
-static void timer_cb(__attribute__((unused)) pico_time t, __attribute__((unused)) void* v)
+static void initial_callbacks(struct pico_hotplug_device *hpdev, int event)
{
- struct pico_tree_node *node = NULL, *safe = NULL, *cb_node = NULL, *cb_safe = NULL;
+ struct pico_tree_node *cb_node = NULL, *cb_safe = NULL;
+ void (*cb)(struct pico_device *dev, int event);
+ pico_tree_foreach_safe(cb_node, &(hpdev->init_callbacks), cb_safe)
+ {
+ cb = cb_node->keyValue;
+ cb(hpdev->dev, event);
+ pico_tree_delete(&hpdev->init_callbacks, cb);
+ }
+}
+
+static void execute_callbacks(struct pico_hotplug_device *hpdev, int new_state, int event)
+{
+ struct pico_tree_node *cb_node = NULL, *cb_safe = NULL;
+ void (*cb)(struct pico_device *dev, int event);
+ if (new_state != hpdev->prev_state)
+ {
+ /* we don't know if one of the callbacks might deregister, so be safe */
+ pico_tree_foreach_safe(cb_node, &(hpdev->callbacks), cb_safe)
+ {
+ cb = cb_node->keyValue;
+ cb(hpdev->dev, event);
+ }
+ hpdev->prev_state = new_state;
+ }
+}
+
+static void timer_cb(__attribute__((unused)) pico_time t, __attribute__((unused)) void*v)
+{
+ struct pico_tree_node *node = NULL, *safe = NULL;
int new_state, event;
struct pico_hotplug_device *hpdev = NULL;
- void (*cb)(struct pico_device *dev, int event);
- //we don't know if one of the callbacks might deregister, so be safe
+ /* 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;
+
+ if (new_state == 1) {
+ event = PICO_HOTPLUG_EVENT_UP;
+ } else {
+ event = PICO_HOTPLUG_EVENT_DOWN;
}
+
+ initial_callbacks(hpdev, event);
+ execute_callbacks(hpdev, new_state, event);
}
timer_id = pico_timer_add(PICO_HOTPLUG_INTERVAL, &timer_cb, NULL);
+ if (timer_id == 0) {
+ dbg("HOTPLUG: Failed to start timer\n");
+ }
}
+static int ensure_hotplug_timer(void)
+{
+ if (timer_id == 0)
+ {
+ timer_id = pico_timer_add(PICO_HOTPLUG_INTERVAL, &timer_cb, NULL);
+ if (timer_id == 0) {
+ dbg("HOTPLUG: Failed to start timer\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void disable_hotplug_timer(void)
+{
+ if (timer_id != 0)
+ {
+ pico_timer_cancel(timer_id);
+ timer_id = 0;
+ }
+}
int pico_hotplug_register(struct pico_device *dev, void (*cb)(struct pico_device *dev, int event))
{
struct pico_hotplug_device *hotplug_dev;
- struct pico_hotplug_device search = {.dev = dev};
+ struct pico_hotplug_device search = {
+ .dev = dev
+ };
- if (dev->link_state == NULL){
+ /* If it does not have a link_state, */
+ /* the device does not support hotplug detection */
+ if (dev->link_state == NULL) {
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
hotplug_dev = (struct pico_hotplug_device*)pico_tree_findKey(&Hotplug_device_tree, &search);
- if (! hotplug_dev )
+ 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);
+ hotplug_dev = PICO_ZALLOC(sizeof(struct pico_hotplug_device));
+ if (!hotplug_dev)
+ {
+ pico_err = PICO_ERR_ENOMEM;
+ return -1;
+ }
- if (timer_id == 0)
- {
- timer_id = pico_timer_add(PICO_HOTPLUG_INTERVAL, &timer_cb, NULL);
+ hotplug_dev->dev = dev;
+ hotplug_dev->prev_state = dev->link_state(hotplug_dev->dev);
+ hotplug_dev->callbacks.root = &LEAF;
+ hotplug_dev->callbacks.compare = &callback_compare;
+ hotplug_dev->init_callbacks.root = &LEAF;
+ hotplug_dev->init_callbacks.compare = &callback_compare;
+ if (pico_tree_insert(&Hotplug_device_tree, hotplug_dev)) {
+ PICO_FREE(hotplug_dev);
+ return -1;
+ }
+ }
+
+ if (pico_tree_insert(&(hotplug_dev->callbacks), cb) == &LEAF) {
+ PICO_FREE(hotplug_dev);
+ return -1;
+ }
+
+ if (pico_tree_insert(&(hotplug_dev->init_callbacks), cb) == &LEAF) {
+ pico_tree_delete(&(hotplug_dev->callbacks), cb);
+ PICO_FREE(hotplug_dev);
+ return -1;
+ }
+
+ if (ensure_hotplug_timer() < 0) {
+ pico_hotplug_deregister((struct pico_device *)hotplug_dev, cb);
+ return -1;
}
return 0;
@@ -110,25 +179,29 @@ int pico_hotplug_register(struct pico_device *dev, void (*cb)(struct pico_device
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};
+ 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
+ /* wasn't registered */
return 0;
+
pico_tree_delete(&hotplug_dev->callbacks, cb);
+ pico_tree_delete(&hotplug_dev->init_callbacks, cb);
if (pico_tree_empty(&hotplug_dev->callbacks))
{
pico_tree_delete(&Hotplug_device_tree, hotplug_dev);
PICO_FREE(hotplug_dev);
}
- if (pico_tree_empty(&Hotplug_device_tree) && timer_id != 0)
+ if (pico_tree_empty(&Hotplug_device_tree))
{
- pico_timer_cancel(timer_id);
- timer_id = 0;
+ disable_hotplug_timer();
}
+
return 0;
}
diff --git a/ext/picotcp/modules/pico_hotplug_detection.h b/ext/picotcp/modules/pico_hotplug_detection.h
index 19319b4..1ab7d85 100644
--- a/ext/picotcp/modules/pico_hotplug_detection.h
+++ b/ext/picotcp/modules/pico_hotplug_detection.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Frederik Van Slycken
*********************************************************************/
@@ -13,6 +13,9 @@
#define PICO_HOTPLUG_INTERVAL 100
+/* register your callback to be notified of hotplug events on a certain device.
+ * Note that each callback will be called at least once, shortly after adding, for initialization.
+ */
int pico_hotplug_register(struct pico_device *dev, void (*cb)(struct pico_device *dev, int event));
int pico_hotplug_deregister(struct pico_device *dev, void (*cb)(struct pico_device *dev, int event));
diff --git a/ext/picotcp/modules/pico_icmp4.c b/ext/picotcp/modules/pico_icmp4.c
index da3d89c..3a8ae17 100644
--- a/ext/picotcp/modules/pico_icmp4.c
+++ b/ext/picotcp/modules/pico_icmp4.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -110,6 +110,11 @@ static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code)
struct pico_ipv4_hdr *info;
uint16_t f_tot_len;
+ if (f == NULL) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
f_tot_len = short_be(((struct pico_ipv4_hdr *)f->net_hdr)->len);
if (f_tot_len < (sizeof(struct pico_ipv4_hdr)))
@@ -120,12 +125,7 @@ static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code)
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));
+ reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, f->dev, (uint16_t) (f_tot_len + PICO_ICMPHDR_UN_SIZE));
info = (struct pico_ipv4_hdr*)(f->net_hdr);
hdr = (struct pico_icmp4_hdr *) reply->transport_hdr;
hdr->type = type;
@@ -225,15 +225,19 @@ static int cookie_compare(void *ka, void *kb)
return (a->seq - b->seq);
}
-PICO_TREE_DECLARE(Pings, cookie_compare);
+static 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_frame *echo = NULL;
struct pico_icmp4_hdr *hdr;
- if (!echo) {
+ struct pico_device *dev = pico_ipv4_source_dev_find(&cookie->dst);
+ if (!dev)
+ return -1;
+
+ echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size));
+ if (!echo)
return -1;
- }
hdr = (struct pico_icmp4_hdr *) echo->transport_hdr;
@@ -274,13 +278,32 @@ static void ping_timeout(pico_time now, void *arg)
}
static void next_ping(pico_time now, void *arg);
-static inline void send_ping(struct pico_icmp4_ping_cookie *cookie)
+static int send_ping(struct pico_icmp4_ping_cookie *cookie)
{
+ uint32_t timeout_timer = 0;
+ struct pico_icmp4_stats stats;
pico_icmp4_send_echo(cookie);
cookie->timestamp = pico_tick;
- 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);
+ timeout_timer = pico_timer_add((uint32_t)cookie->timeout, ping_timeout, cookie);
+ if (!timeout_timer) {
+ goto fail;
+ }
+ if (cookie->seq < (uint16_t)cookie->count) {
+ if (!pico_timer_add((uint32_t)cookie->interval, next_ping, cookie)) {
+ pico_timer_cancel(timeout_timer);
+ goto fail;
+ }
+ }
+ return 0;
+
+fail:
+ dbg("ICMP4: Failed to start timer\n");
+ cookie->err = PICO_PING_ERR_ABORTED;
+ stats.err = cookie->err;
+ cookie->cb(&stats);
+ pico_tree_delete(&Pings, cookie);
+
+ return -1;
}
static void next_ping(pico_time now, void *arg)
@@ -300,8 +323,16 @@ static void next_ping(pico_time now, void *arg)
memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie));
newcookie->seq++;
- pico_tree_insert(&Pings, newcookie);
- send_ping(newcookie);
+ if (pico_tree_insert(&Pings, newcookie)) {
+ dbg("ICMP4: Failed to insert new cookie in tree \n");
+ PICO_FREE(newcookie);
+ return;
+ }
+
+ if (send_ping(newcookie)) {
+ dbg("ICMP4: Failed to send ping\n");
+ PICO_FREE(newcookie);
+ }
}
}
}
@@ -365,8 +396,16 @@ int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, v
cookie->cb = cb;
cookie->count = count;
- pico_tree_insert(&Pings, cookie);
- send_ping(cookie);
+ if (pico_tree_insert(&Pings, cookie)) {
+ dbg("ICMP4: Failed to insert cookie in tree \n");
+ PICO_FREE(cookie);
+ return -1;
+ }
+
+ if (send_ping(cookie)) {
+ PICO_FREE(cookie);
+ return -1;
+ }
return cookie->id;
diff --git a/ext/picotcp/modules/pico_icmp4.h b/ext/picotcp/modules/pico_icmp4.h
index 45f0a62..7a3a5e8 100644
--- a/ext/picotcp/modules/pico_icmp4.h
+++ b/ext/picotcp/modules/pico_icmp4.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
diff --git a/ext/picotcp/modules/pico_icmp6.c b/ext/picotcp/modules/pico_icmp6.c
index 3c46e7e..fdf6a98 100644
--- a/ext/picotcp/modules/pico_icmp6.c
+++ b/ext/picotcp/modules/pico_icmp6.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -10,24 +10,38 @@
#include "pico_config.h"
#include "pico_icmp6.h"
#include "pico_ipv6_nd.h"
+#include "pico_6lowpan.h"
#include "pico_eth.h"
#include "pico_device.h"
#include "pico_stack.h"
#include "pico_tree.h"
#include "pico_socket.h"
#include "pico_mld.h"
-#define icmp6_dbg(...) do { }while(0);
+
+#ifdef DEBUG_ICMP6
+ #define icmp6_dbg dbg
+#else
+ #define icmp6_dbg(...) do { } while(0)
+#endif
static struct pico_queue icmp6_in;
static struct pico_queue icmp6_out;
+/******************************************************************************
+ * Function prototypes
+ ******************************************************************************/
+
+#ifdef PICO_SUPPORT_6LOWPAN
+static int pico_6lp_nd_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst);
+#endif
+
uint16_t pico_icmp6_checksum(struct pico_frame *f)
{
struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
-
+
struct pico_icmp6_hdr *icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
struct pico_ipv6_pseudo_hdr pseudo;
-
+
pseudo.src = ipv6_hdr->src;
pseudo.dst = ipv6_hdr->dst;
pseudo.len = long_be(f->transport_len);
@@ -51,7 +65,7 @@ static int pico_icmp6_send_echoreply(struct pico_frame *echo)
struct pico_ip6 src;
struct pico_ip6 dst;
- reply = pico_proto_ipv6.alloc(&pico_proto_ipv6, (uint16_t)(echo->transport_len));
+ reply = pico_proto_ipv6.alloc(&pico_proto_ipv6, echo->dev, (uint16_t)(echo->transport_len));
if (!reply) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -60,7 +74,6 @@ static int pico_icmp6_send_echoreply(struct pico_frame *echo)
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;
@@ -105,13 +118,13 @@ static int pico_icmp6_process_in(struct pico_protocol *self, struct pico_frame *
#endif
pico_frame_discard(f);
break;
-#ifdef PICO_SUPPORT_MCAST
+#if defined(PICO_SUPPORT_MCAST) && defined(PICO_SUPPORT_MLD)
case PICO_MLD_QUERY:
case PICO_MLD_REPORT:
case PICO_MLD_DONE:
case PICO_MLD_REPORTV2:
pico_mld_process_in(f);
- break;
+ break;
#endif
default:
return pico_ipv6_nd_recv(f); /* CAUTION -- Implies: pico_frame_discard in any case, keep in the default! */
@@ -156,7 +169,7 @@ static int pico_icmp6_notify(struct pico_frame *f, uint8_t type, uint8_t code, u
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));
+ notice = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, (uint16_t)(PICO_ICMP6HDR_DEST_UNREACH_SIZE + len));
if (!notice) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -173,7 +186,7 @@ static int pico_icmp6_notify(struct pico_frame *f, uint8_t type, uint8_t code, u
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));
+ notice = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, (uint16_t)(PICO_ICMP6HDR_TIME_XCEEDED_SIZE + len));
if (!notice) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -189,7 +202,7 @@ static int pico_icmp6_notify(struct pico_frame *f, uint8_t type, uint8_t code, u
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));
+ notice = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, (uint16_t)(PICO_ICMP6HDR_PARAM_PROBLEM_SIZE + len));
if (!notice) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -208,7 +221,6 @@ static int pico_icmp6_notify(struct pico_frame *f, uint8_t type, uint8_t code, u
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;
@@ -280,60 +292,175 @@ MOCKABLE int pico_icmp6_frag_expired(struct pico_frame *f)
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)
+/* Provide a Link-Layer Address Option, either Source (SLLAO) or Destination (DLLAO) */
+static int pico_icmp6_provide_llao(struct pico_icmp6_opt_lladdr *llao, uint8_t type, struct pico_device *dev, struct pico_ip6 *src)
{
- 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;
+#ifdef PICO_SUPPORT_6LOWPAN
+ struct pico_6lowpan_info *info = (struct pico_6lowpan_info *)dev->eth;
+#endif
+ IGNORE_PARAMETER(src);
+ llao->type = type;
- 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;
+ if (!dev->mode && dev->eth) {
+ memcpy(llao->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
+ llao->len = 1;
+ }
+#ifdef PICO_SUPPORT_6LOWPAN
+ else if (PICO_DEV_IS_6LOWPAN(dev) && dev->eth) {
+ if (src && IID_16(&src->addr[8])) {
+ memcpy(llao->addr.pan.data, (uint8_t *)&info->addr_short.addr, SIZE_6LOWPAN_SHORT);
+ memset(llao->addr.pan.data + SIZE_6LOWPAN_SHORT, 0, 4);
+ llao->len = 1;
+ } else {
+ memcpy(llao->addr.pan.data, info->addr_ext.addr, SIZE_6LOWPAN_EXT);
+ memset(llao->addr.pan.data + SIZE_6LOWPAN_EXT, 0, 6);
+ llao->len = 2;
+ }
+ }
+#endif
+ else {
return -1;
}
- 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;
}
+/* Prepares a ICMP6 neighbor solicitation message */
+static struct pico_frame *pico_icmp6_neigh_sol_prep(struct pico_device *dev, struct pico_ip6 *dst, uint16_t len)
+{
+ struct pico_icmp6_hdr *icmp = NULL;
+ struct pico_frame *sol = NULL;
+ IGNORE_PARAMETER(dev);
+
+ /* Create pico_frame to contain the Neighbor Solicitation */
+ sol = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
+ if (!sol) {
+ pico_err = PICO_ERR_ENOMEM;
+ return NULL;
+ }
+ sol->payload = sol->transport_hdr + len;
+ sol->payload_len = 0;
+ icmp = (struct pico_icmp6_hdr *)sol->transport_hdr;
+ icmp->type = PICO_ICMP6_NEIGH_SOL;
+ icmp->code = 0;
+ icmp->msg.info.neigh_sol.unused = 0;
+ icmp->msg.info.neigh_sol.target = *dst;
+ return sol;
+}
+
+/* RFC 4861 $7.2.2: sending neighbor solicitations */
+int pico_icmp6_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst)
+{
+ struct pico_ip6 daddr = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00 }};
+ struct pico_icmp6_opt_lladdr *llao = NULL;
+ struct pico_icmp6_hdr *icmp = NULL;
+ struct pico_frame *sol = NULL;
+ uint8_t i = 0;
+ uint16_t len = 0;
+#ifndef PICO_SUPPORT_6LOWPAN
+ IGNORE_PARAMETER(dst);
+#endif
+
+ if (pico_ipv6_is_multicast(tgt->addr)) {
+ return -1;
+ }
+#ifdef PICO_SUPPORT_6LOWPAN
+ else if (PICO_DEV_IS_6LOWPAN(dev)) {
+ return pico_6lp_nd_neighbor_solicitation(dev, tgt, type, dst);
+ }
+#endif
+ else {
+ /* Determine the size frame needs to be for the Neighbor Solicitation */
+ len = PICO_ICMP6HDR_NEIGH_SOL_SIZE;
+ if (PICO_ICMP6_ND_DAD != type)
+ len = (uint16_t)(len + 8);
+
+ /* Prepare a neighbor solicitation message */
+ sol = pico_icmp6_neigh_sol_prep(dev, tgt, len);
+ if (sol) {
+ icmp = (struct pico_icmp6_hdr *)sol->transport_hdr;
+
+ /* Provide SLLAO if it's neighbor solicitation for DAD */
+ llao = (struct pico_icmp6_opt_lladdr *)(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s));
+ if (PICO_ICMP6_ND_DAD != type && pico_icmp6_provide_llao(llao, PICO_ND_OPT_LLADDR_SRC, dev, NULL)) {
+ pico_frame_discard(sol);
+ return -1;
+ } else {
+ /* Determine destination address */
+ if (type == PICO_ICMP6_ND_SOLICITED || type == PICO_ICMP6_ND_DAD) {
+ for (i = 1; i <= 3; ++i)
+ daddr.addr[PICO_SIZE_IP6 - i] = tgt->addr[PICO_SIZE_IP6 - i];
+ } else {
+ daddr = *tgt;
+ }
+
+ sol->dev = dev;
+ /* f->src is set in frame_push, checksum calculated there */
+ pico_ipv6_frame_push(sol, NULL, &daddr, PICO_PROTO_ICMP6, (type == PICO_ICMP6_ND_DAD));
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+#ifdef PICO_SUPPORT_6LOWPAN
+/* Provide an Address Registration Option */
+static void pico_6lp_nd_provide_aro(struct pico_icmp6_opt_aro *aro, struct pico_device *dev, uint8_t type)
+{
+ struct pico_6lowpan_info *info = (struct pico_6lowpan_info *)dev->eth;
+ aro->type = PICO_ND_OPT_ARO;
+ aro->len = 2;
+ aro->status = 0;
+ if (PICO_ICMP6_ND_DEREGISTER == type)
+ aro->lifetime = 0;
+ else
+ aro->lifetime = short_be(PICO_6LP_ND_DEFAULT_LIFETIME);
+ memcpy(aro->eui64.addr, info->addr_ext.addr, SIZE_6LOWPAN_EXT);
+}
+
+/* Send an ICMP6 neighbor solicitation according to RFC6775 */
+static int pico_6lp_nd_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst)
+{
+ uint32_t llao_len = IID_16(&tgt->addr[8]) ? 8 : 16;
+ struct pico_icmp6_opt_lladdr *llao = NULL;
+ struct pico_icmp6_opt_aro *aro = NULL;
+ struct pico_icmp6_hdr *icmp = NULL;
+ struct pico_frame *sol = NULL;
+ uint16_t len = 0;
+
+ /* Determine the size frame needs to be for the Neighbor Solicitation */
+ len = (uint16_t)(PICO_ICMP6HDR_NEIGH_SOL_SIZE + llao_len);
+ if (PICO_ICMP6_ND_DAD == type)
+ len = (uint16_t)(len + sizeof(struct pico_icmp6_opt_aro));
+
+ /* Prepare a neighbor solicitation message */
+ sol = pico_icmp6_neigh_sol_prep(dev, tgt, len);
+ if (sol) {
+ icmp = (struct pico_icmp6_hdr *)sol->transport_hdr;
+
+ /* Provide SLLAO if it's a neighbor solicitation for address registration */
+ llao = (struct pico_icmp6_opt_lladdr *)(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s));
+ if (pico_icmp6_provide_llao(llao, PICO_ND_OPT_LLADDR_SRC, dev, NULL)) {
+ pico_frame_discard(sol);
+ return -1;
+ } else {
+ /* Provide ARO when it's a neighbor solicitation for address registration or re-registration */
+ aro = (struct pico_icmp6_opt_aro *)(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s) + llao_len);
+ pico_6lp_nd_provide_aro(aro, dev, type);
+
+ /* RFC6775: The address that is to be registered MUST be the IPv6 source address of the
+ * NS message. */
+ sol->dev = dev;
+ pico_ipv6_frame_push(sol, tgt, dst, PICO_PROTO_ICMP6, (type == PICO_ICMP6_ND_DAD));
+ return 0;
+ }
+ }
+ return -1;
+}
+#endif
+
/* RFC 4861 $7.2.4: sending solicited neighbor advertisements */
int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *target)
{
@@ -344,7 +471,7 @@ int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *tar
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);
+ adv = pico_proto_ipv6.alloc(&pico_proto_ipv6, f->dev, PICO_ICMP6HDR_NEIGH_ADV_SIZE + 8);
if (!adv) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -380,7 +507,6 @@ int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *tar
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);
@@ -388,19 +514,26 @@ int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *tar
}
/* RFC 4861 $6.3.7: sending router solicitations */
-int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src)
+int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src, struct pico_ip6 *dst)
{
- 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 }};
+ struct pico_icmp6_opt_lladdr *lladdr = NULL;
+ struct pico_icmp6_hdr *icmp6_hdr = NULL;
+ struct pico_frame *sol = NULL;
+ uint16_t len = 0;
len = PICO_ICMP6HDR_ROUTER_SOL_SIZE;
- if (!pico_ipv6_is_unspecified(src->addr))
+ if (!pico_ipv6_is_unspecified(src->addr)) {
len = (uint16_t)(len + 8);
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (PICO_DEV_IS_6LOWPAN(dev))
+ len = (uint16_t)(len + 8);
+ } else if (PICO_DEV_IS_6LOWPAN(dev) && pico_ipv6_is_unspecified(src->addr)) {
+ return -1; /* RFC6775 (6LoWPAN): An unspecified source address MUST NOT be used in RS messages. */
+#endif
+ }
- sol = pico_proto_ipv6.alloc(&pico_proto_ipv6, len);
+ sol = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
if (!sol) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -414,36 +547,62 @@ int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src
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);
+ lladdr = (struct pico_icmp6_opt_lladdr *)((uint8_t *)&icmp6_hdr->msg.info.router_sol + sizeof(struct router_sol_s));
+ if (pico_icmp6_provide_llao(lladdr, PICO_ND_OPT_LLADDR_SRC, dev, NULL)) {
+ pico_frame_discard(sol);
+ return -1;
+ }
}
sol->dev = dev;
- /* f->src is set in frame_push, checksum calculated there */
- pico_ipv6_frame_push(sol, NULL, &daddr, PICO_PROTO_ICMP6, 0);
+ if (!dev->mode) {
+ /* f->src is set in frame_push, checksum calculated there */
+ pico_ipv6_frame_push(sol, NULL, &daddr, PICO_PROTO_ICMP6, 0);
+ }
+#ifdef PICO_SUPPORT_6LOWPAN
+ else {
+ if (dst)
+ daddr = *dst;
+ /* Force this frame to be send with the EUI-64-address */
+ pico_ipv6_frame_push(sol, src, &daddr, PICO_PROTO_ICMP6, 0);
+ }
+#else
+ IGNORE_PARAMETER(dst);
+#endif
return 0;
}
#define PICO_RADV_VAL_LIFETIME (long_be(86400))
#define PICO_RADV_PREF_LIFETIME (long_be(14400))
+static struct pico_ip6 pico_icmp6_address_to_prefix(struct pico_ip6 addr, struct pico_ip6 nm)
+{
+ struct pico_ip6 prefix;
+ uint8_t i = 0;
+
+ for (i = 0; i < PICO_SIZE_IP6; i++) {
+ prefix.addr[i] = (uint8_t)(addr.addr[i] & nm.addr[i]);
+ }
+
+ return prefix;
+}
+
/* RFC 4861: sending router advertisements */
int pico_icmp6_router_advertisement(struct pico_device *dev, struct pico_ip6 *dst)
{
struct pico_frame *adv = NULL;
+ struct pico_ip6 prefix_addr = {{ 0x00 }};
struct pico_icmp6_hdr *icmp6_hdr = NULL;
struct pico_icmp6_opt_lladdr *lladdr;
struct pico_icmp6_opt_prefix *prefix;
+ struct pico_ipv6_link *global = NULL;
uint16_t len = 0;
- 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);
+ adv = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
if (!adv) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -451,7 +610,6 @@ int pico_icmp6_router_advertisement(struct pico_device *dev, struct pico_ip6 *ds
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;
@@ -468,17 +626,29 @@ int pico_icmp6_router_advertisement(struct pico_device *dev, struct pico_ip6 *ds
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));
+ /* Find the globally routable prefix of the router-interface */
+ if ((global = pico_ipv6_global_get(dev))) {
+ prefix_addr = pico_icmp6_address_to_prefix(global->address, global->netmask);
+ memcpy(&prefix->prefix, &prefix_addr, sizeof(struct pico_ip6));
+ }
nxt_opt += (sizeof (struct pico_icmp6_opt_prefix));
lladdr = (struct pico_icmp6_opt_lladdr *)nxt_opt;
+
lladdr->type = PICO_ND_OPT_LLADDR_SRC;
- lladdr->len = 1;
- memcpy(lladdr->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
+
+ if (!dev->mode && dev->eth) {
+ lladdr->len = 1;
+ memcpy(lladdr->addr.mac.addr, dev->eth->mac.addr, PICO_SIZE_ETH);
+ } else {
+
+ return -1;
+ }
+
icmp6_hdr->crc = 0;
icmp6_hdr->crc = short_be(pico_icmp6_checksum(adv));
/* f->src is set in frame_push, checksum calculated there */
- pico_ipv6_frame_push(adv, NULL, &dst_mcast, PICO_PROTO_ICMP6, 0);
+ pico_ipv6_frame_push(adv, NULL, dst, PICO_PROTO_ICMP6, 0);
return 0;
}
@@ -513,14 +683,14 @@ static int icmp6_cookie_compare(void *ka, void *kb)
return (a->seq - b->seq);
}
-PICO_TREE_DECLARE(IPV6Pings, icmp6_cookie_compare);
+static PICO_TREE_DECLARE(IPV6Pings, icmp6_cookie_compare);
static int pico_icmp6_send_echo(struct pico_icmp6_ping_cookie *cookie)
{
struct pico_frame *echo = NULL;
struct pico_icmp6_hdr *hdr = NULL;
- echo = pico_proto_ipv6.alloc(&pico_proto_ipv6, (uint16_t)(PICO_ICMP6HDR_ECHO_REQUEST_SIZE + cookie->size));
+ echo = pico_proto_ipv6.alloc(&pico_proto_ipv6, cookie->dev, (uint16_t)(PICO_ICMP6HDR_ECHO_REQUEST_SIZE + cookie->size));
if (!echo) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -537,7 +707,6 @@ static int pico_icmp6_send_echo(struct pico_icmp6_ping_cookie *cookie)
/* 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;
}
@@ -571,12 +740,30 @@ static void pico_icmp6_ping_timeout(pico_time now, void *arg)
}
static void pico_icmp6_next_ping(pico_time now, void *arg);
-static inline void pico_icmp6_send_ping(struct pico_icmp6_ping_cookie *cookie)
+static int pico_icmp6_send_ping(struct pico_icmp6_ping_cookie *cookie)
{
+ uint32_t interval_timer = 0;
+ struct pico_icmp6_stats stats;
pico_icmp6_send_echo(cookie);
cookie->timestamp = pico_tick;
- pico_timer_add((pico_time)(cookie->interval), pico_icmp6_next_ping, cookie);
- pico_timer_add((pico_time)(cookie->timeout), pico_icmp6_ping_timeout, cookie);
+ interval_timer = pico_timer_add((pico_time)(cookie->interval), pico_icmp6_next_ping, cookie);
+ if (!interval_timer) {
+ goto fail;
+ }
+ if (!pico_timer_add((pico_time)(cookie->timeout), pico_icmp6_ping_timeout, cookie)) {
+ pico_timer_cancel(interval_timer);
+ goto fail;
+ }
+ return 0;
+
+fail:
+ dbg("ICMP6: Failed to start timer\n");
+ cookie->err = PICO_PING6_ERR_ABORTED;
+ stats.err = cookie->err;
+ cookie->cb(&stats);
+ pico_tree_delete(&IPV6Pings, cookie);
+
+ return -1;
}
static void pico_icmp6_next_ping(pico_time now, void *arg)
@@ -600,8 +787,16 @@ static void pico_icmp6_next_ping(pico_time now, void *arg)
memcpy(new, cookie, sizeof(struct pico_icmp6_ping_cookie));
new->seq++;
- pico_tree_insert(&IPV6Pings, new);
- pico_icmp6_send_ping(new);
+ if (pico_tree_insert(&IPV6Pings, new)) {
+ dbg("ICMP6: Failed to insert new cookie in tree\n");
+ PICO_FREE(new);
+ return;
+ }
+
+ if (pico_icmp6_send_ping(new)) {
+ dbg("ICMP6: Failed to send ping\n");
+ PICO_FREE(new);
+ }
}
}
}
@@ -670,8 +865,16 @@ int pico_icmp6_ping(char *dst, int count, int interval, int timeout, int size, v
cookie->count = count;
cookie->dev = dev;
- pico_tree_insert(&IPV6Pings, cookie);
- pico_icmp6_send_ping(cookie);
+ if (pico_tree_insert(&IPV6Pings, cookie)) {
+ dbg("ICMP6: Failed to insert cookie in tree\n");
+ PICO_FREE(cookie);
+ return -1;
+ }
+
+ if (pico_icmp6_send_ping(cookie)) {
+ PICO_FREE(cookie);
+ return -1;
+ }
return (int)cookie->id;
}
diff --git a/ext/picotcp/modules/pico_icmp6.h b/ext/picotcp/modules/pico_icmp6.h
index 7734060..b449075 100644
--- a/ext/picotcp/modules/pico_icmp6.h
+++ b/ext/picotcp/modules/pico_icmp6.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -19,6 +19,7 @@
#define PICO_ICMP6HDR_NEIGH_SOL_SIZE 24
#define PICO_ICMP6HDR_NEIGH_ADV_SIZE 24
#define PICO_ICMP6HDR_ROUTER_SOL_SIZE 8
+#define PICO_ICMP6HDR_ROUTER_SOL_SIZE_6LP 16
#define PICO_ICMP6HDR_ROUTER_ADV_SIZE 16
#define PICO_ICMP6HDR_REDIRECT_SIZE 40
@@ -76,6 +77,9 @@
#define PICO_ND_OPT_REDIRECT 4
#define PICO_ND_OPT_MTU 5
#define PICO_ND_OPT_RDNSS 25 /* RFC 5006 */
+#define PICO_ND_OPT_ARO 33 /* RFC 6775 */
+#define PICO_ND_OPT_6CO 34 /* RFC 6775 */
+#define PICO_ND_OPT_ABRO 35 /* RFC 6775 */
/* ND advertisement flags */
#define PICO_ND_ROUTER 0x80000000
@@ -93,12 +97,19 @@
#define PICO_ICMP6_ND_ANYCAST 1
#define PICO_ICMP6_ND_SOLICITED 2
#define PICO_ICMP6_ND_DAD 3
+#define PICO_ICMP6_ND_DEREGISTER 4
#define PICO_ICMP6_MAX_RTR_SOL_DELAY 1000
-#define PICO_SIZE_ICMP6HDR ((sizeof(struct pico_icmp6_hdr)))
#define PICO_ICMP6_OPT_LLADDR_SIZE (8)
+/******************************************************************************
+ * 6LoWPAN Constants
+ ******************************************************************************/
+
+/* Address registration lifetime */
+#define PICO_6LP_ND_DEFAULT_LIFETIME (120) /* TWO HOURS */
+
extern struct pico_protocol pico_proto_icmp6;
PACKED_STRUCT_DEF pico_icmp6_hdr {
@@ -156,27 +167,45 @@ PACKED_STRUCT_DEF pico_icmp6_hdr {
struct pico_ip6 target;
struct pico_ip6 dest;
} redirect;
- PEDANTIC_STRUCT_DEF mld_s {
+ 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 reserverd; /* With S and QRV */
uint8_t QQIC;
uint16_t nbr_src;
- struct pico_ip6 src[0];
- } mld;
+ struct pico_ip6 src[1];
+ } mld;
+ /* 6LoWPAN Duplicate Address Message */
+ PEDANTIC_STRUCT_DEF da_s {
+ uint8_t status;
+ uint8_t reserved;
+ uint16_t lifetime;
+ struct pico_6lowpan_ext eui64;
+ struct pico_ip6 addr;
+ } da;
} info;
} msg;
};
+PACKED_UNION_DEF pico_hw_addr {
+ struct pico_eth mac;
+#ifdef PICO_SUPPORT_6LOWPAN
+ union pico_6lowpan_u pan;
+#endif /* PICO_SUPPORT_6LOWPAN */
+ uint8_t data[8];
+};
+
+/******************************************************************************
+ * ICMP6 Neighbor Discovery Options
+ ******************************************************************************/
+
PACKED_STRUCT_DEF pico_icmp6_opt_lladdr
{
uint8_t type;
uint8_t len;
- PACKED_UNION_DEF icmp6_opt_hw_addr_u {
- struct pico_eth mac;
- } addr;
+ union pico_hw_addr addr;
};
PACKED_STRUCT_DEF pico_icmp6_opt_prefix
@@ -224,6 +253,46 @@ PACKED_STRUCT_DEF pico_icmp6_opt_na
uint8_t len;
};
+/* 6LoWPAN Address Registration Option (ARO) */
+PACKED_STRUCT_DEF pico_icmp6_opt_aro
+{
+ uint8_t type;
+ uint8_t len;
+ uint8_t status;
+ uint8_t res0;
+ uint16_t res1;
+ uint16_t lifetime;
+ struct pico_6lowpan_ext eui64;
+};
+
+#define ICMP6_ARO_SUCCES (0u)
+#define ICMP6_ARO_DUP (1u)
+#define ICMP6_ARO_FULL (2u)
+
+/* 6LoWPAN Context Option (6CO) */
+PACKED_STRUCT_DEF pico_icmp6_opt_6co
+{
+ uint8_t type;
+ uint8_t len;
+ uint8_t clen;
+ uint8_t id: 4;
+ uint8_t res: 3;
+ uint8_t c: 1;
+ uint16_t lifetime;
+ uint8_t prefix;
+};
+
+/* 6LoWPAN Authoritative Border Router Option (ABRO) */
+PACKED_STRUCT_DEF pico_icmp6_opt_abro
+{
+ uint8_t type;
+ uint8_t len;
+ uint16_t version_low;
+ uint16_t version_high;
+ uint16_t lifetime;
+ struct pico_ip6 addr;
+};
+
struct pico_icmp6_stats
{
unsigned long size;
@@ -237,9 +306,10 @@ struct pico_icmp6_stats
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_solicitation(struct pico_device *dev, struct pico_ip6 *tgt, uint8_t type, struct pico_ip6 *dst);
int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *target);
-int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src);
+int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src, struct pico_ip6 *dst);
int pico_icmp6_port_unreachable(struct pico_frame *f);
int pico_icmp6_proto_unreachable(struct pico_frame *f);
diff --git a/ext/picotcp/modules/pico_igmp.c b/ext/picotcp/modules/pico_igmp.c
index 2d2fc08..49f8358 100644
--- a/ext/picotcp/modules/pico_igmp.c
+++ b/ext/picotcp/modules/pico_igmp.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
RFC 1112, 2236, 3376, 3569, 3678, 4607
@@ -17,11 +17,15 @@
#include "pico_tree.h"
#include "pico_device.h"
#include "pico_socket.h"
+#include "pico_mcast.h"
-#if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST)
+#if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST)
-#define igmp_dbg(...) do {} while(0)
-/* #define igmp_dbg dbg */
+#ifdef DEBUG_IGMP
+ #define igmp_dbg dbg
+#else
+ #define igmp_dbg(...) do {} while(0)
+#endif
/* membership states */
#define IGMP_STATE_NON_MEMBER (0x0)
@@ -113,18 +117,6 @@ PACKED_STRUCT_DEF igmpv3_report {
uint16_t groups;
};
-struct igmp_parameters {
- uint8_t event;
- uint8_t state;
- uint8_t last_host;
- uint8_t filter_mode;
- uint8_t max_resp_time;
- struct pico_ip4 mcast_link;
- struct pico_ip4 mcast_group;
- struct pico_tree *MCASTFilter;
- struct pico_frame *f;
-};
-
struct igmp_timer {
uint8_t type;
uint8_t stopped;
@@ -145,10 +137,10 @@ static struct pico_queue igmp_out = {
};
/* finite state machine caller */
-static int pico_igmp_process_event(struct igmp_parameters *p);
+static int pico_igmp_process_event(struct mcast_parameters *p);
/* state callback prototype */
-typedef int (*callback)(struct igmp_parameters *);
+typedef int (*callback)(struct mcast_parameters *);
static inline int igmpt_type_compare(struct igmp_timer *a, struct igmp_timer *b)
{
@@ -187,51 +179,51 @@ static int igmp_timer_cmp(void *ka, void *kb)
return igmpt_link_compare(a, b);
}
-PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp);
+static PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp);
-static inline int igmpparm_group_compare(struct igmp_parameters *a, struct igmp_parameters *b)
+static inline int igmpparm_group_compare(struct mcast_parameters *a, struct mcast_parameters *b)
{
- return pico_ipv4_compare(&a->mcast_group, &b->mcast_group);
+ return pico_ipv4_compare(&a->mcast_group.ip4, &b->mcast_group.ip4);
}
-static inline int igmpparm_link_compare(struct igmp_parameters *a, struct igmp_parameters *b)
+static inline int igmpparm_link_compare(struct mcast_parameters *a, struct mcast_parameters *b)
{
- return pico_ipv4_compare(&a->mcast_link, &b->mcast_link);
+ return pico_ipv4_compare(&a->mcast_link.ip4, &b->mcast_link.ip4);
}
static int igmp_parameters_cmp(void *ka, void *kb)
{
- struct igmp_parameters *a = ka, *b = kb;
+ struct mcast_parameters *a = ka, *b = kb;
int cmp = igmpparm_group_compare(a, b);
if (cmp)
return cmp;
return igmpparm_link_compare(a, b);
}
-PICO_TREE_DECLARE(IGMPParameters, igmp_parameters_cmp);
+static PICO_TREE_DECLARE(IGMPParameters, igmp_parameters_cmp);
static int igmp_sources_cmp(void *ka, void *kb)
{
struct pico_ip4 *a = ka, *b = kb;
return pico_ipv4_compare(a, b);
}
-PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp);
-PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp);
+static PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp);
+static PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp);
-static struct igmp_parameters *pico_igmp_find_parameter(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
+static struct mcast_parameters *pico_igmp_find_parameter(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
{
- struct igmp_parameters test = {
+ struct mcast_parameters test = {
0
};
if (!mcast_link || !mcast_group)
return NULL;
- test.mcast_link.addr = mcast_link->addr;
- test.mcast_group.addr = mcast_group->addr;
+ test.mcast_link.ip4 = *mcast_link;
+ test.mcast_group.ip4 = *mcast_group;
return pico_tree_findKey(&IGMPParameters, &test);
}
-static int pico_igmp_delete_parameter(struct igmp_parameters *p)
+static int pico_igmp_delete_parameter(struct mcast_parameters *p)
{
if (pico_tree_delete(&IGMPParameters, p))
PICO_FREE(p);
@@ -272,7 +264,11 @@ static void pico_igmp_timer_expired(pico_time now, void *arg)
PICO_FREE(timer);
} else {
igmp_dbg("IGMP: restart timer for %08X, delay %lu, new delay %lu\n", t->mcast_group.addr, t->delay, (timer->start + timer->delay) - PICO_TIME_MS());
- pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_igmp_timer_expired, timer);
+ if (!pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_igmp_timer_expired, timer)) {
+ igmp_dbg("IGMP: Failed to start expiration timer\n");
+ pico_tree_delete(&IGMPTimers, timer);
+ PICO_FREE(timer);
+ }
}
return;
@@ -320,8 +316,18 @@ static int pico_igmp_timer_start(struct igmp_timer *t)
*timer = *t;
timer->start = PICO_TIME_MS();
- pico_tree_insert(&IGMPTimers, timer);
- pico_timer_add(timer->delay, &pico_igmp_timer_expired, timer);
+ if (pico_tree_insert(&IGMPTimers, timer)) {
+ igmp_dbg("IGMP: Failed to insert timer in tree\n");
+ PICO_FREE(timer);
+ return -1;
+ }
+
+ if (!pico_timer_add(timer->delay, &pico_igmp_timer_expired, timer)) {
+ igmp_dbg("IGMP: Failed to start expiration timer\n");
+ pico_tree_delete(&IGMPTimers, timer);
+ PICO_FREE(timer);
+ return -1;
+ }
return 0;
}
@@ -373,7 +379,7 @@ static struct igmp_timer *pico_igmp_find_timer(uint8_t type, struct pico_ip4 *mc
static void pico_igmp_report_expired(struct igmp_timer *t)
{
- struct igmp_parameters *p = NULL;
+ struct mcast_parameters *p = NULL;
p = pico_igmp_find_parameter(&t->mcast_link, &t->mcast_group);
if (!p)
@@ -431,6 +437,11 @@ static int pico_igmp_compatibility_mode(struct pico_frame *f)
0
};
uint8_t ihl = 24, datalen = 0;
+ struct igmp_message *message = NULL;
+ struct mcast_parameters *p = NULL;
+ struct pico_ip4 mcast_group = {
+ 0
+ };
link = pico_ipv4_link_by_dev(f->dev);
if (!link)
@@ -454,6 +465,7 @@ static int pico_igmp_compatibility_mode(struct pico_frame *f)
}
} else if (datalen == 8) {
struct igmp_message *query = (struct igmp_message *)f->transport_hdr;
+ /* Check if max_resp_time is set RFC 3376 $7.1 */
if (query->max_resp_time != 0) {
/* IGMPv2 query */
/* When changing compatibility mode, cancel all pending response
@@ -466,12 +478,22 @@ static int pico_igmp_compatibility_mode(struct pico_frame *f)
}
igmp_dbg("IGMP: switch to compatibility mode IGMPv2\n");
link->mcast_compatibility = PICO_IGMPV2;
+ /* Reset the event and state to prevent deadlock */
+ message = (struct igmp_message *)f->transport_hdr;
+ mcast_group.addr = message->mcast_group;
+ p = pico_igmp_find_parameter(&link->address, &mcast_group);
+ if(p) {
+ p->state = IGMP_STATE_NON_MEMBER;
+ p->event = IGMP_EVENT_CREATE_GROUP;
+ }
+
t.type = IGMP_TIMER_V2_QUERIER;
t.delay = ((IGMP_ROBUSTNESS * link->mcast_last_query_interval) + IGMP_QUERY_RESPONSE_INTERVAL) * 1000;
t.f = f;
t.callback = pico_igmp_v2querier_expired;
/* only one of this type of timer may exist! */
- pico_igmp_timer_start(&t);
+ if (pico_igmp_timer_start(&t) < 0)
+ return -1;
} else {
/* IGMPv1 query, not supported */
return -1;
@@ -484,10 +506,10 @@ static int pico_igmp_compatibility_mode(struct pico_frame *f)
return 0;
}
-static struct igmp_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
+static struct mcast_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
{
struct igmp_message *message = NULL;
- struct igmp_parameters *p = NULL;
+ struct mcast_parameters *p = NULL;
struct pico_ipv4_link *link = NULL;
struct pico_ip4 mcast_group = {
0
@@ -502,14 +524,18 @@ static struct igmp_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
mcast_group.addr = message->mcast_group;
p = pico_igmp_find_parameter(&link->address, &mcast_group);
if (!p && mcast_group.addr == 0) { /* general query */
- p = PICO_ZALLOC(sizeof(struct igmp_parameters));
+ p = PICO_ZALLOC(sizeof(struct mcast_parameters));
if (!p)
return NULL;
p->state = IGMP_STATE_NON_MEMBER;
- p->mcast_link.addr = link->address.addr;
- p->mcast_group.addr = mcast_group.addr;
- pico_tree_insert(&IGMPParameters, p);
+ p->mcast_link.ip4 = link->address;
+ p->mcast_group.ip4 = mcast_group;
+ if (pico_tree_insert(&IGMPParameters, p)) {
+ igmp_dbg("IGMP: Failed to insert parameters in tree\n");
+ PICO_FREE(p);
+ return NULL;
+ }
} else if (!p) {
return NULL;
}
@@ -538,19 +564,19 @@ static struct igmp_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f)
{
- struct igmp_parameters *p = NULL;
+ struct mcast_parameters *p = NULL;
IGNORE_PARAMETER(self);
if (!pico_igmp_is_checksum_valid(f))
goto out;
- if (pico_igmp_compatibility_mode(f) < 0)
- goto out;
-
p = pico_igmp_analyse_packet(f);
if (!p)
goto out;
+ if (pico_igmp_compatibility_mode(f) < 0)
+ goto out;
+
return pico_igmp_process_event(p);
out:
@@ -579,28 +605,33 @@ 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)
{
- struct igmp_parameters *p = NULL;
+ struct mcast_parameters *p = NULL;
+
+ if (!mcast_link || !mcast_group) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
if (mcast_group->addr == IGMP_ALL_HOST_GROUP)
return 0;
p = pico_igmp_find_parameter(mcast_link, mcast_group);
if (!p && state == PICO_IGMP_STATE_CREATE) {
- p = PICO_ZALLOC(sizeof(struct igmp_parameters));
+ p = PICO_ZALLOC(sizeof(struct mcast_parameters));
if (!p) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
- if (!mcast_link || !mcast_group) {
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
-
p->state = IGMP_STATE_NON_MEMBER;
- p->mcast_link = *mcast_link;
- p->mcast_group = *mcast_group;
- pico_tree_insert(&IGMPParameters, p);
+ p->mcast_link.ip4 = *mcast_link;
+ p->mcast_group.ip4 = *mcast_group;
+ if (pico_tree_insert(&IGMPParameters, p)) {
+ igmp_dbg("IGMP: Failed to insert parameters in tree\n");
+ PICO_FREE(p);
+ return -1;
+ }
+
} else if (!p) {
pico_err = PICO_ERR_EINVAL;
return -1;
@@ -628,7 +659,7 @@ int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_g
return pico_igmp_process_event(p);
}
-static int pico_igmp_send_report(struct igmp_parameters *p, struct pico_frame *f)
+static int pico_igmp_send_report(struct mcast_parameters *p, struct pico_frame *f)
{
struct pico_ip4 dst = {
0
@@ -638,11 +669,11 @@ static int pico_igmp_send_report(struct igmp_parameters *p, struct pico_frame *f
};
struct pico_ipv4_link *link = NULL;
- link = pico_ipv4_link_get(&p->mcast_link);
+ link = pico_ipv4_link_get((struct pico_ip4*)&p->mcast_link);
if (!link)
return -1;
- mcast_group.addr = p->mcast_group.addr;
+ mcast_group = p->mcast_group.ip4;
switch (link->mcast_compatibility) {
case PICO_IGMPV2:
if (p->event == IGMP_EVENT_DELETE_GROUP)
@@ -665,285 +696,138 @@ static int pico_igmp_send_report(struct igmp_parameters *p, struct pico_frame *f
pico_ipv4_frame_push(f, &dst, PICO_PROTO_IGMP);
return 0;
}
-
-static int8_t pico_igmp_generate_report(struct igmp_parameters *p)
+static int8_t pico_igmpv3_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
{
- struct pico_ipv4_link *link = NULL;
- int i = 0;
-
- link = pico_ipv4_link_get(&p->mcast_link);
- if (!link) {
+ struct pico_mcast_group *g = NULL, test = {
+ 0
+ };
+ struct pico_tree *IGMPFilter = NULL;
+ struct pico_ipv4_link *link = (struct pico_ipv4_link*) filter->link;
+ filter->p = (struct mcast_parameters *)p;
+ filter->allow = &IGMPAllow;
+ filter->block = &IGMPBlock;
+ filter->filter = IGMPFilter;
+ filter->sources = 0;
+ filter->proto = PICO_IGMPV3;
+ test.mcast_addr = p->mcast_group;
+ g = pico_tree_findKey(link->MCASTGroups, &test);
+ if (!g) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
- switch (link->mcast_compatibility) {
+ filter->g = (struct pico_mcast_group *)g;
+ return pico_mcast_generate_filter(filter, p);
+}
+static int8_t pico_igmpv3_generate_report(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
+{
+ struct igmpv3_report *report = NULL;
+ struct igmpv3_group_record *record = NULL;
+ struct pico_tree_node *index = NULL;
+ struct pico_device *dev = NULL;
+ uint16_t len = 0;
+ uint16_t i = 0;
+ len = (uint16_t)(sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (filter->sources * sizeof(struct pico_ip4)));
+ dev = pico_ipv4_link_find((struct pico_ip4 *)&p->mcast_link);
+ p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(IP_OPTION_ROUTER_ALERT_LEN + len));
+ p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
+ p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
+ p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
+ /* p->f->len is correctly set by alloc */
+
+ report = (struct igmpv3_report *)p->f->transport_hdr;
+ report->type = IGMP_TYPE_MEM_REPORT_V3;
+ report->res0 = 0;
+ report->crc = 0;
+ report->res1 = 0;
+ report->groups = short_be(1);
+
+ record = (struct igmpv3_group_record *)(((uint8_t *)report) + sizeof(struct igmpv3_report));
+ record->type = filter->record_type;
+ record->aux = 0;
+ record->sources = short_be(filter->sources);
+ record->mcast_group = p->mcast_group.ip4.addr;
+ if (filter->filter && !pico_tree_empty(filter->filter)) {
+ uint32_t *source_addr = (uint32_t *)((uint8_t *)record + sizeof(struct igmpv3_group_record));
+ i = 0;
+ pico_tree_foreach(index, filter->filter)
+ {
+ source_addr[i] = ((struct pico_ip4 *)index->keyValue)->addr;
+ i++;
+ }
+ }
+
+ if(i != filter->sources) {
+ return -1;
+ }
+
+ report->crc = short_be(pico_checksum(report, len));
+ return 0;
+}
+static int8_t pico_igmpv2_generate_report(struct mcast_parameters *p)
+{
+ struct igmp_message *report = NULL;
+ uint8_t report_type = IGMP_TYPE_MEM_REPORT_V2;
+ struct pico_device *dev = NULL;
+ if (p->event == IGMP_EVENT_DELETE_GROUP)
+ report_type = IGMP_TYPE_LEAVE_GROUP;
+
+ dev = pico_ipv4_link_find((struct pico_ip4 *)&p->mcast_link);
+ p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, IP_OPTION_ROUTER_ALERT_LEN + sizeof(struct igmp_message));
+ p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
+ p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
+ p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
+ /* p->f->len is correctly set by alloc */
+
+ report = (struct igmp_message *)p->f->transport_hdr;
+ report->type = report_type;
+ report->max_resp_time = IGMP_DEFAULT_MAX_RESPONSE_TIME;
+ report->mcast_group = p->mcast_group.ip4.addr;
+
+ report->crc = 0;
+ report->crc = short_be(pico_checksum(report, sizeof(struct igmp_message)));
+ return 0;
+}
+static int8_t pico_igmp_generate_report(struct mcast_parameters *p)
+{
+ struct mcast_filter_parameters filter;
+ int8_t result;
+
+ filter.link = (union pico_link *)pico_ipv4_link_get((struct pico_ip4 *) &p->mcast_link);
+ if (!filter.link) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+ switch (filter.link->ipv4.mcast_compatibility) {
case PICO_IGMPV1:
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
case PICO_IGMPV2:
{
- struct igmp_message *report = NULL;
- uint8_t report_type = IGMP_TYPE_MEM_REPORT_V2;
- if (p->event == IGMP_EVENT_DELETE_GROUP)
- report_type = IGMP_TYPE_LEAVE_GROUP;
-
- p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, IP_OPTION_ROUTER_ALERT_LEN + sizeof(struct igmp_message));
- p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
- p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
- p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
- p->f->dev = pico_ipv4_link_find(&p->mcast_link);
- /* p->f->len is correctly set by alloc */
-
- report = (struct igmp_message *)p->f->transport_hdr;
- report->type = report_type;
- report->max_resp_time = IGMP_DEFAULT_MAX_RESPONSE_TIME;
- report->mcast_group = p->mcast_group.addr;
-
- report->crc = 0;
- report->crc = short_be(pico_checksum(report, sizeof(struct igmp_message)));
- break;
+ return pico_igmpv2_generate_report(p);
}
case PICO_IGMPV3:
{
- struct igmpv3_report *report = NULL;
- struct igmpv3_group_record *record = NULL;
- struct pico_mcast_group *g = NULL, test = {
- 0
- };
- struct pico_tree_node *index = NULL, *_tmp = NULL;
- struct pico_tree *IGMPFilter = NULL;
- struct pico_ip4 *source = NULL;
- uint8_t record_type = 0;
- uint8_t sources = 0;
- uint16_t len = 0;
-
- test.mcast_addr = p->mcast_group;
- g = pico_tree_findKey(link->MCASTGroups, &test);
- if (!g) {
- pico_err = PICO_ERR_EINVAL;
+ result = pico_igmpv3_generate_filter(&filter, p);
+ if(result < 0)
return -1;
- }
- if (p->event == IGMP_EVENT_DELETE_GROUP) { /* "non-existent" state of filter mode INCLUDE and empty source list */
- p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
- p->MCASTFilter = NULL;
- }
-
- if (p->event == IGMP_EVENT_QUERY_RECV) {
- goto igmp3_report;
- }
-
-
- /* cleanup filters */
- pico_tree_foreach_safe(index, &IGMPAllow, _tmp)
- {
- pico_tree_delete(&IGMPAllow, index->keyValue);
- }
- pico_tree_foreach_safe(index, &IGMPBlock, _tmp)
- {
- pico_tree_delete(&IGMPBlock, index->keyValue);
- }
-
- switch (g->filter_mode) {
-
- case PICO_IP_MULTICAST_INCLUDE:
- switch (p->filter_mode) {
- case PICO_IP_MULTICAST_INCLUDE:
- if (p->event == IGMP_EVENT_DELETE_GROUP) { /* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
- /* TO_IN (B) */
- record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
- IGMPFilter = &IGMPAllow;
- if (p->MCASTFilter) {
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&IGMPAllow, index->keyValue);
- sources++;
- }
- } /* else { IGMPAllow stays empty } */
-
- break;
- }
-
- /* ALLOW (B-A) */
- /* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
- if (p->event == IGMP_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
- record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
- else
- record_type = IGMP_ALLOW_NEW_SOURCES;
-
- IGMPFilter = &IGMPAllow;
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&IGMPAllow, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, &g->MCASTSources) /* A */
- {
- source = pico_tree_findKey(&IGMPAllow, index->keyValue);
- if (source) {
- pico_tree_delete(&IGMPAllow, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
- break;
-
- /* BLOCK (A-B) */
- record_type = IGMP_BLOCK_OLD_SOURCES;
- IGMPFilter = &IGMPBlock;
- pico_tree_foreach(index, &g->MCASTSources) /* A */
- {
- pico_tree_insert(&IGMPBlock, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- source = pico_tree_findKey(&IGMPBlock, index->keyValue);
- if (source) {
- pico_tree_delete(&IGMPBlock, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
- break;
-
- /* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report (RFC 3376 $5.1) */
- p->f = NULL;
- return 0;
-
- case PICO_IP_MULTICAST_EXCLUDE:
- /* TO_EX (B) */
- record_type = IGMP_CHANGE_TO_EXCLUDE_MODE;
- IGMPFilter = &IGMPBlock;
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&IGMPBlock, index->keyValue);
- sources++;
- }
- break;
-
- default:
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
- break;
-
- case PICO_IP_MULTICAST_EXCLUDE:
- switch (p->filter_mode) {
- case PICO_IP_MULTICAST_INCLUDE:
- /* TO_IN (B) */
- record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
- IGMPFilter = &IGMPAllow;
- if (p->MCASTFilter) {
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&IGMPAllow, index->keyValue);
- sources++;
- }
- } /* else { IGMPAllow stays empty } */
-
- break;
-
- case PICO_IP_MULTICAST_EXCLUDE:
- /* BLOCK (B-A) */
- record_type = IGMP_BLOCK_OLD_SOURCES;
- IGMPFilter = &IGMPBlock;
- pico_tree_foreach(index, p->MCASTFilter)
- {
- pico_tree_insert(&IGMPBlock, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, &g->MCASTSources) /* A */
- {
- source = pico_tree_findKey(&IGMPBlock, index->keyValue); /* B */
- if (source) {
- pico_tree_delete(&IGMPBlock, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
- break;
-
- /* ALLOW (A-B) */
- record_type = IGMP_ALLOW_NEW_SOURCES;
- IGMPFilter = &IGMPAllow;
- pico_tree_foreach(index, &g->MCASTSources)
- {
- pico_tree_insert(&IGMPAllow, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- source = pico_tree_findKey(&IGMPAllow, index->keyValue); /* A */
- if (source) {
- pico_tree_delete(&IGMPAllow, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
- break;
-
- /* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report (RFC 3376 $5.1) */
- p->f = NULL;
- return 0;
-
- default:
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
- break;
-
- default:
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
-
-igmp3_report:
- len = (uint16_t)(sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (sources * sizeof(struct pico_ip4)));
- p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t)(IP_OPTION_ROUTER_ALERT_LEN + len));
- p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
- p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
- p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
- p->f->dev = pico_ipv4_link_find(&p->mcast_link);
- /* p->f->len is correctly set by alloc */
-
- report = (struct igmpv3_report *)p->f->transport_hdr;
- report->type = IGMP_TYPE_MEM_REPORT_V3;
- report->res0 = 0;
- report->crc = 0;
- report->res1 = 0;
- report->groups = short_be(1);
-
- record = (struct igmpv3_group_record *)(((uint8_t *)report) + sizeof(struct igmpv3_report));
- record->type = record_type;
- record->aux = 0;
- record->sources = short_be(sources);
- record->mcast_group = p->mcast_group.addr;
- if (IGMPFilter && !pico_tree_empty(IGMPFilter)) {
- uint32_t *source_addr = (uint32_t *)((void *)((uint8_t *)record + sizeof(struct igmpv3_group_record)));
- i = 0;
- pico_tree_foreach(index, IGMPFilter)
- {
- source_addr[i] = ((struct pico_ip4 *)index->keyValue)->addr;
- i++;
- }
- }
-
- report->crc = short_be(pico_checksum(report, len));
- break;
+ if(result != MCAST_NO_REPORT)
+ return pico_igmpv3_generate_report(&filter, p);
}
-
+ break;
default:
pico_err = PICO_ERR_EINVAL;
return -1;
}
+
return 0;
}
/* stop timer, send leave if flag set */
-static int stslifs(struct igmp_parameters *p)
+static int stslifs(struct mcast_parameters *p)
{
struct igmp_timer t = {
0
@@ -952,11 +836,13 @@ static int stslifs(struct igmp_parameters *p)
igmp_dbg("IGMP: event = leave group | action = stop timer, send leave if flag set\n");
t.type = IGMP_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip4;
+ t.mcast_group = p->mcast_group.ip4;
if (pico_igmp_timer_stop(&t) < 0)
return -1;
+ if(pico_igmp_generate_report(p) < 0)
+ return -1;
/* always send leave, even if not last host */
if (pico_igmp_send_report(p, p->f) < 0)
return -1;
@@ -967,7 +853,7 @@ static int stslifs(struct igmp_parameters *p)
}
/* send report, set flag, start timer */
-static int srsfst(struct igmp_parameters *p)
+static int srsfst(struct mcast_parameters *p)
{
struct igmp_timer t = {
0
@@ -994,12 +880,13 @@ static int srsfst(struct igmp_parameters *p)
return -1;
t.type = IGMP_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip4;
+ t.mcast_group = p->mcast_group.ip4;
t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
t.f = p->f;
t.callback = pico_igmp_report_expired;
- pico_igmp_timer_start(&t);
+ if (pico_igmp_timer_start(&t) < 0)
+ return -1;
p->state = IGMP_STATE_DELAYING_MEMBER;
igmp_dbg("IGMP: new state = delaying member\n");
@@ -1007,7 +894,7 @@ static int srsfst(struct igmp_parameters *p)
}
/* merge report, send report, reset timer (IGMPv3 only) */
-static int mrsrrt(struct igmp_parameters *p)
+static int mrsrrt(struct mcast_parameters *p)
{
struct igmp_timer *t = NULL;
struct pico_frame *copy_frame = NULL;
@@ -1015,7 +902,7 @@ static int mrsrrt(struct igmp_parameters *p)
igmp_dbg("IGMP: event = update group | action = merge report, send report, reset timer (IGMPv3 only)\n");
- link = pico_ipv4_link_get(&p->mcast_link);
+ link = pico_ipv4_link_get((struct pico_ip4 *)&p->mcast_link);
if (!link)
return -1;
@@ -1033,7 +920,7 @@ static int mrsrrt(struct igmp_parameters *p)
if (pico_igmp_send_report(p, copy_frame) < 0)
return -1;
- t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
+ t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link.ip4, &p->mcast_group.ip4);
if (!t)
return -1;
@@ -1046,7 +933,7 @@ static int mrsrrt(struct igmp_parameters *p)
}
/* send report, start timer (IGMPv3 only) */
-static int srst(struct igmp_parameters *p)
+static int srst(struct mcast_parameters *p)
{
struct igmp_timer t = {
0
@@ -1056,7 +943,7 @@ static int srst(struct igmp_parameters *p)
igmp_dbg("IGMP: event = update group | action = send report, start timer (IGMPv3 only)\n");
- link = pico_ipv4_link_get(&p->mcast_link);
+ link = pico_ipv4_link_get(&p->mcast_link.ip4);
if (!link)
return -1;
@@ -1079,12 +966,13 @@ static int srst(struct igmp_parameters *p)
return -1;
t.type = IGMP_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip4;
+ t.mcast_group = p->mcast_group.ip4;
t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
t.f = p->f;
t.callback = pico_igmp_report_expired;
- pico_igmp_timer_start(&t);
+ if (pico_igmp_timer_start(&t) < 0)
+ return -1;
p->state = IGMP_STATE_DELAYING_MEMBER;
igmp_dbg("IGMP: new state = delaying member\n");
@@ -1092,11 +980,13 @@ static int srst(struct igmp_parameters *p)
}
/* send leave if flag set */
-static int slifs(struct igmp_parameters *p)
+static int slifs(struct mcast_parameters *p)
{
igmp_dbg("IGMP: event = leave group | action = send leave if flag set\n");
/* always send leave, even if not last host */
+ if(pico_igmp_generate_report(p) < 0)
+ return -1;
if (pico_igmp_send_report(p, p->f) < 0)
return -1;
@@ -1106,7 +996,7 @@ static int slifs(struct igmp_parameters *p)
}
/* start timer */
-static int st(struct igmp_parameters *p)
+static int st(struct mcast_parameters *p)
{
struct igmp_timer t = {
0
@@ -1125,12 +1015,13 @@ static int st(struct igmp_parameters *p)
}
t.type = IGMP_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip4;
+ t.mcast_group = p->mcast_group.ip4;
t.delay = (pico_rand() % ((1u + p->max_resp_time) * 100u));
t.f = p->f;
t.callback = pico_igmp_report_expired;
- pico_igmp_timer_start(&t);
+ if (pico_igmp_timer_start(&t) < 0)
+ return -1;
p->state = IGMP_STATE_DELAYING_MEMBER;
igmp_dbg("IGMP: new state = delaying member\n");
@@ -1138,7 +1029,7 @@ static int st(struct igmp_parameters *p)
}
/* stop timer, clear flag */
-static int stcl(struct igmp_parameters *p)
+static int stcl(struct mcast_parameters *p)
{
struct igmp_timer t = {
0
@@ -1147,8 +1038,8 @@ static int stcl(struct igmp_parameters *p)
igmp_dbg("IGMP: event = report received | action = stop timer, clear flag\n");
t.type = IGMP_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip4;
+ t.mcast_group = p->mcast_group.ip4;
if (pico_igmp_timer_stop(&t) < 0)
return -1;
@@ -1159,7 +1050,7 @@ static int stcl(struct igmp_parameters *p)
}
/* send report, set flag */
-static int srsf(struct igmp_parameters *p)
+static int srsf(struct mcast_parameters *p)
{
igmp_dbg("IGMP: event = timer expired | action = send report, set flag\n");
@@ -1172,14 +1063,14 @@ static int srsf(struct igmp_parameters *p)
}
/* reset timer if max response time < current timer */
-static int rtimrtct(struct igmp_parameters *p)
+static int rtimrtct(struct mcast_parameters *p)
{
struct igmp_timer *t = NULL;
uint32_t time_to_run = 0;
igmp_dbg("IGMP: event = query received | action = reset timer if max response time < current timer\n");
- t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
+ t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link.ip4, &p->mcast_group.ip4);
if (!t)
return -1;
@@ -1194,7 +1085,7 @@ static int rtimrtct(struct igmp_parameters *p)
return 0;
}
-static int discard(struct igmp_parameters *p)
+static int discard(struct mcast_parameters *p)
{
igmp_dbg("IGMP: ignore and discard frame\n");
pico_frame_discard(p->f);
@@ -1202,7 +1093,7 @@ static int discard(struct igmp_parameters *p)
}
/* finite state machine table */
-const callback host_membership_diagram_table[3][6] =
+static const callback host_membership_diagram_table[3][6] =
{ /* event |Delete Group |Create Group |Update Group |Query Received |Report Received |Timer Expired */
/* state Non-Member */
{ discard, srsfst, srsfst, discard, discard, discard },
@@ -1210,18 +1101,18 @@ const callback host_membership_diagram_table[3][6] =
/* state Idle Member */ { slifs, srst, srst, st, discard, discard }
};
-static int pico_igmp_process_event(struct igmp_parameters *p)
+static int pico_igmp_process_event(struct mcast_parameters *p)
{
struct pico_tree_node *index = NULL;
- struct igmp_parameters *_p = NULL;
+ struct mcast_parameters *_p = NULL;
- igmp_dbg("IGMP: process event on group address %08X\n", p->mcast_group.addr);
- if (p->event == IGMP_EVENT_QUERY_RECV && p->mcast_group.addr == 0) { /* general query */
+ igmp_dbg("IGMP: process event on group address %08X\n", p->mcast_group.ip4.addr);
+ if (p->event == IGMP_EVENT_QUERY_RECV && p->mcast_group.ip4.addr == 0) { /* general query */
pico_tree_foreach(index, &IGMPParameters) {
_p = index->keyValue;
_p->max_resp_time = p->max_resp_time;
_p->event = IGMP_EVENT_QUERY_RECV;
- igmp_dbg("IGMP: for each mcast_group = %08X | state = %u\n", _p->mcast_group.addr, _p->state);
+ igmp_dbg("IGMP: for each mcast_group = %08X | state = %u\n", _p->mcast_group.ip4.addr, _p->state);
host_membership_diagram_table[_p->state][_p->event](_p);
}
} else {
@@ -1240,14 +1131,16 @@ static struct pico_queue igmp_out = {
0
};
-static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f) {
+static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
IGNORE_PARAMETER(self);
IGNORE_PARAMETER(f);
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
-static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f) {
+static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
IGNORE_PARAMETER(self);
IGNORE_PARAMETER(f);
return -1;
@@ -1264,7 +1157,8 @@ struct pico_protocol pico_proto_igmp = {
.q_out = &igmp_out,
};
-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) {
+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)
+{
IGNORE_PARAMETER(mcast_link);
IGNORE_PARAMETER(mcast_group);
IGNORE_PARAMETER(filter_mode);
diff --git a/ext/picotcp/modules/pico_igmp.h b/ext/picotcp/modules/pico_igmp.h
index 6f8d74e..58aa6a9 100644
--- a/ext/picotcp/modules/pico_igmp.h
+++ b/ext/picotcp/modules/pico_igmp.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
diff --git a/ext/picotcp/modules/pico_ipfilter.c b/ext/picotcp/modules/pico_ipfilter.c
index 64ee349..e28775e 100644
--- a/ext/picotcp/modules/pico_ipfilter.c
+++ b/ext/picotcp/modules/pico_ipfilter.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Andrei Carp
Simon Maes
@@ -22,7 +22,11 @@
#define MAX_PRIORITY (10)
#define MIN_PRIORITY (-10)
-#define ipf_dbg(...) do {} while(0)
+#ifdef DEBUG_IPF
+ #define ipf_dbg dbg
+#else
+ #define ipf_dbg(...) do {} while(0)
+#endif
/**************** LOCAL DECLARATIONS ****************/
struct filter_node;
@@ -49,7 +53,7 @@ struct filter_node {
int (*function_ptr)(struct filter_node *filter, struct pico_frame *f);
};
-PICO_TREE_DECLARE(filter_tree, &filter_compare);
+static PICO_TREE_DECLARE(filter_tree, &filter_compare);
static inline int ipfilter_uint32_cmp(uint32_t a, uint32_t b)
{
@@ -401,7 +405,9 @@ uint32_t pico_ipv4_filter_add(struct pico_device *dev, uint8_t proto,
int pico_ipv4_filter_del(uint32_t filter_id)
{
struct filter_node *node = NULL;
- struct filter_node dummy = { 0 };
+ struct filter_node dummy = {
+ 0
+ };
dummy.filter_id = filter_id;
if((node = pico_tree_delete(&filter_tree, &dummy)) == NULL)
@@ -446,7 +452,7 @@ int ipfilter(struct pico_frame *f)
}
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)
+ if(icmp_hdr->type == PICO_ICMP_UNREACH && icmp_hdr->code == PICO_ICMP_UNREACH_FILTER_PROHIB)
return 0;
}
diff --git a/ext/picotcp/modules/pico_ipfilter.h b/ext/picotcp/modules/pico_ipfilter.h
index fb92e67..c966b28 100644
--- a/ext/picotcp/modules/pico_ipfilter.h
+++ b/ext/picotcp/modules/pico_ipfilter.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Simon Maes
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_ipv4.c b/ext/picotcp/modules/pico_ipv4.c
index 11317e9..2dbfe23 100644
--- a/ext/picotcp/modules/pico_ipv4.c
+++ b/ext/picotcp/modules/pico_ipv4.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera, Markian Yskout
*********************************************************************/
@@ -22,20 +22,25 @@
#include "pico_aodv.h"
#include "pico_socket_multicast.h"
#include "pico_fragments.h"
+#include "pico_ethernet.h"
+#include "pico_mcast.h"
+
+#include "../../../include/Debug.hpp"
#ifdef PICO_SUPPORT_IPV4
#ifdef PICO_SUPPORT_MCAST
-# define ip_mcast_dbg(...) do {} while(0) /* so_mcast_dbg in pico_socket.c */
-/* #define ip_mcast_dbg dbg */
+
+#ifdef DEBUG_MCAST
+#define ip_mcast_dbg dbg
+#else
+#define ip_mcast_dbg(...) do {} while(0)
+#endif
+
# define PICO_MCAST_ALL_HOSTS long_be(0xE0000001) /* 224.0.0.1 */
/* Default network interface for multicast transmission */
static struct pico_ipv4_link *mcast_default_link = NULL;
#endif
-#ifdef PICO_SUPPORT_IPV4FRAG
-/* # define reassembly_dbg dbg */
-# define reassembly_dbg(...) do {} while(0)
-#endif
/* Queues */
static struct pico_queue in = {
@@ -47,7 +52,7 @@ static struct pico_queue out = {
/* Functions */
static int ipv4_route_compare(void *ka, void *kb);
-static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, uint16_t size);
+static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size);
int pico_ipv4_compare(struct pico_ip4 *a, struct pico_ip4 *b)
@@ -284,7 +289,7 @@ static int ipv4_link_compare(void *ka, void *kb)
return 0;
}
-PICO_TREE_DECLARE(Tree_dev_link, ipv4_link_compare);
+static PICO_TREE_DECLARE(Tree_dev_link, ipv4_link_compare);
static int pico_ipv4_process_bcast_in(struct pico_frame *f)
{
@@ -364,7 +369,7 @@ static int pico_ipv4_process_local_unicast_in(struct pico_frame *f)
static void pico_ipv4_process_finally_try_forward(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
- if ((pico_ipv4_is_broadcast(hdr->dst.addr)) || ((f->flags & PICO_FRAME_FLAG_BCAST)!= 0)) {
+ if ((pico_ipv4_is_broadcast(hdr->dst.addr)) || ((f->flags & PICO_FRAME_FLAG_BCAST) != 0)) {
/* don't forward broadcast frame, discard! */
pico_frame_discard(f);
} else if (pico_ipv4_forward(f) != 0) {
@@ -381,12 +386,12 @@ static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f
int ret = 0;
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
uint16_t max_allowed = (uint16_t) ((int)f->buffer_len - (f->net_hdr - f->buffer) - (int)PICO_SIZE_IP4HDR);
- uint16_t flag = short_be(hdr->frag);
-
- (void)self;
if (!hdr)
return -1;
+
+ (void)self;
+
/* NAT needs transport header information */
if (((hdr->vhl) & 0x0F) > 5) {
option_len = (uint8_t)(4 * (((hdr->vhl) & 0x0F) - 5));
@@ -395,6 +400,9 @@ static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f
f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR + option_len;
f->transport_len = (uint16_t)(short_be(hdr->len) - PICO_SIZE_IP4HDR - option_len);
f->net_len = (uint16_t)(PICO_SIZE_IP4HDR + option_len);
+#if defined(PICO_SUPPORT_IPV4FRAG) || defined(PICO_SUPPORT_IPV6FRAG)
+ f->frag = short_be(hdr->frag);
+#endif
if (f->transport_len > max_allowed) {
pico_frame_discard(f);
@@ -421,11 +429,13 @@ static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f
return 0;
}
- if (hdr->frag & short_be(PICO_IPV4_EVIL)) {
+#if defined(PICO_SUPPORT_IPV4FRAG) || defined(PICO_SUPPORT_IPV6FRAG)
+ if (f->frag & PICO_IPV4_EVIL) {
(void)pico_icmp4_param_problem(f, 0);
pico_frame_discard(f); /* RFC 3514 */
return 0;
}
+#endif
if ((hdr->vhl & 0x0f) < 5) {
/* RFC 791: IHL minimum value is 5 */
@@ -434,16 +444,18 @@ static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f
return 0;
}
- if (flag & (PICO_IPV4_MOREFRAG | PICO_IPV4_FRAG_MASK))
+#if defined(PICO_SUPPORT_IPV4FRAG) || defined(PICO_SUPPORT_IPV6FRAG)
+ if (f->frag & (PICO_IPV4_MOREFRAG | PICO_IPV4_FRAG_MASK))
{
#ifdef PICO_SUPPORT_IPV4FRAG
- pico_ipv4_process_frag(hdr, f, hdr ? hdr->proto : 0 );
+ pico_ipv4_process_frag(hdr, f, hdr->proto);
/* Frame can be discarded, frag will handle its own copy */
#endif
/* We do not support fragmentation, discard quietly */
pico_frame_discard(f);
return 0;
}
+#endif
if (pico_ipv4_process_bcast_in(f) > 0)
return 0;
@@ -473,24 +485,28 @@ static int pico_ipv4_process_out(struct pico_protocol *self, struct pico_frame *
}
#endif
- return pico_sendto_dev(f);
+ return pico_datalink_send(f);
}
-static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, uint16_t size)
+static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size)
{
- struct pico_frame *f = pico_frame_alloc(size + PICO_SIZE_IP4HDR + PICO_SIZE_ETHHDR);
+ struct pico_frame *f = NULL;
IGNORE_PARAMETER(self);
+ f = pico_proto_ethernet.alloc(&pico_proto_ethernet, dev, (uint16_t)(size + PICO_SIZE_IP4HDR));
+ /* TODO: In 6LoWPAN topic branch update to make use of dev->ll_mode */
+
if (!f)
return NULL;
- f->datalink_hdr = f->buffer;
- f->net_hdr = f->buffer + PICO_SIZE_ETHHDR;
f->net_len = PICO_SIZE_IP4HDR;
f->transport_hdr = f->net_hdr + PICO_SIZE_IP4HDR;
- f->transport_len = size;
- f->len = size + PICO_SIZE_IP4HDR;
+ f->transport_len = (uint16_t)size;
+
+ /* Datalink size is accounted for in pico_datalink_send (link layer) */
+ f->len = (uint32_t)(size + PICO_SIZE_IP4HDR);
+
return f;
}
@@ -559,6 +575,10 @@ static struct pico_ipv4_route *route_find(const struct pico_ip4 *addr)
struct pico_ipv4_route *r;
struct pico_tree_node *index;
+ if (addr->addr == PICO_IP4_ANY) {
+ return NULL;
+ }
+
if (addr->addr != PICO_IP4_BCAST) {
pico_tree_foreach_reverse(index, &Routes) {
r = index->keyValue;
@@ -598,10 +618,6 @@ struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst)
struct pico_ipv4_route *rt;
#ifdef PICO_SUPPORT_AODV
union pico_address node_address;
- node_address.ip4.addr = dst->addr;
- if (dst->addr && pico_ipv4_is_unicast(dst->addr))
- pico_aodv_lookup(&node_address);
-
#endif
if (!dst) {
@@ -609,6 +625,13 @@ struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst)
return NULL;
}
+#ifdef PICO_SUPPORT_AODV
+ node_address.ip4.addr = dst->addr;
+ if (dst->addr && pico_ipv4_is_unicast(dst->addr))
+ pico_aodv_lookup(&node_address);
+
+#endif
+
rt = route_find(dst);
if (rt && rt->link) {
myself = &rt->link->address;
@@ -657,7 +680,7 @@ struct pico_device *pico_ipv4_source_dev_find(const struct pico_ip4 *dst)
static int ipv4_mcast_groups_cmp(void *ka, void *kb)
{
struct pico_mcast_group *a = ka, *b = kb;
- return pico_ipv4_compare(&a->mcast_addr, &b->mcast_addr);
+ return pico_ipv4_compare(&a->mcast_addr.ip4, &b->mcast_addr.ip4);
}
static int ipv4_mcast_sources_cmp(void *ka, void *kb)
@@ -682,7 +705,7 @@ static void pico_ipv4_mcast_print_groups(struct pico_ipv4_link *mcast_link)
pico_tree_foreach(index, mcast_link->MCASTGroups) {
g = index->keyValue;
- ip_mcast_dbg("+ %04d | %16s | %08X | %05u | %u | %8s +\n", i, mcast_link->dev->name, g->mcast_addr.addr, g->reference_count, g->filter_mode, "");
+ ip_mcast_dbg("+ %04d | %16s | %08X | %05u | %u | %8s +\n", i, mcast_link->dev->name, g->mcast_addr.ip4.addr, g->reference_count, g->filter_mode, "");
pico_tree_foreach(index2, &g->MCASTSources) {
source = index2->keyValue;
ip_mcast_dbg("+ %4s | %16s | %8s | %5s | %s | %08X +\n", "", "", "", "", "", source->addr);
@@ -711,9 +734,12 @@ static int mcast_group_update(struct pico_mcast_group *g, struct pico_tree *MCAS
pico_err = PICO_ERR_ENOMEM;
return -1;
}
-
source->addr = ((struct pico_ip4 *)index->keyValue)->addr;
- pico_tree_insert(&g->MCASTSources, source);
+ if (pico_tree_insert(&g->MCASTSources, source)) {
+ dbg("IPv4: Failed to insert source in tree\n");
+ PICO_FREE(source);
+ return -1;
+ }
}
}
}
@@ -735,13 +761,15 @@ int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_gro
if (!link)
link = mcast_default_link;
- test.mcast_addr = *mcast_group;
+ test.mcast_addr.ip4 = *mcast_group;
g = pico_tree_findKey(link->MCASTGroups, &test);
if (g) {
if (reference_count)
g->reference_count++;
+#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
+#endif
} else {
g = PICO_ZALLOC(sizeof(struct pico_mcast_group));
if (!g) {
@@ -752,11 +780,18 @@ int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_gro
/* "non-existent" state of filter mode INCLUDE and empty source list */
g->filter_mode = PICO_IP_MULTICAST_INCLUDE;
g->reference_count = 1;
- g->mcast_addr = *mcast_group;
+ g->mcast_addr.ip4 = *mcast_group;
g->MCASTSources.root = &LEAF;
g->MCASTSources.compare = ipv4_mcast_sources_cmp;
- pico_tree_insert(link->MCASTGroups, g);
+ if (pico_tree_insert(link->MCASTGroups, g)) {
+ dbg("IPv4: Failed to insert group in tree\n");
+ PICO_FREE(g);
+ return -1;
+ }
+
+#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_CREATE);
+#endif
}
if (mcast_group_update(g, MCASTFilter, filter_mode) < 0) {
@@ -787,14 +822,16 @@ int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_gr
if (!link)
return -1;
- test.mcast_addr = *mcast_group;
+ test.mcast_addr.ip4 = *mcast_group;
g = pico_tree_findKey(link->MCASTGroups, &test);
if (!g) {
pico_err = PICO_ERR_EINVAL;
return -1;
} else {
if (reference_count && (--(g->reference_count) < 1)) {
+#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_DELETE);
+#endif
/* cleanup filter */
pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) {
source = index->keyValue;
@@ -804,7 +841,9 @@ int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_gr
pico_tree_delete(link->MCASTGroups, g);
PICO_FREE(g);
} else {
+#ifdef PICO_SUPPORT_IGMP
pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
+#endif
if (mcast_group_update(g, MCASTFilter, filter_mode) < 0)
return -1;
}
@@ -828,7 +867,7 @@ static int pico_ipv4_mcast_filter(struct pico_frame *f)
};
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
- test.mcast_addr = hdr->dst;
+ test.mcast_addr.ip4 = hdr->dst;
pico_tree_foreach(index, &Tree_dev_link) {
link = index->keyValue;
@@ -875,12 +914,22 @@ static int pico_ipv4_mcast_filter(struct pico_frame *f)
int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
{
+ IGNORE_PARAMETER(mcast_link);
+ IGNORE_PARAMETER(mcast_group);
+ IGNORE_PARAMETER(reference_count);
+ IGNORE_PARAMETER(filter_mode);
+ IGNORE_PARAMETER(MCASTFilter);
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
{
+ IGNORE_PARAMETER(mcast_link);
+ IGNORE_PARAMETER(mcast_group);
+ IGNORE_PARAMETER(reference_count);
+ IGNORE_PARAMETER(filter_mode);
+ IGNORE_PARAMETER(MCASTFilter);
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
@@ -920,6 +969,7 @@ int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t pro
struct pico_ipv4_hdr *hdr;
uint8_t ttl = PICO_IPV4_DEFAULT_TTL;
uint8_t vhl = 0x45; /* version 4, header length 20 */
+ int32_t retval = 0;
static uint16_t ipv4_progressive_id = 0x91c0;
#ifdef PICO_SUPPORT_MCAST
struct pico_tree_node *index;
@@ -927,7 +977,7 @@ int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t pro
if (!f || !dst) {
pico_err = PICO_ERR_EINVAL;
- return -1;
+ goto drop;
}
@@ -994,7 +1044,7 @@ int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t pro
if (
#ifdef PICO_SUPPORT_IPV4FRAG
- (0 == (f->frag & PICO_IPV4_MOREFRAG)) &&
+ (0 == (f->frag & PICO_IPV4_MOREFRAG)) &&
#endif
1 )
ipv4_progressive_id++;
@@ -1046,7 +1096,15 @@ int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t pro
if ((proto != PICO_PROTO_IGMP) && (pico_ipv4_mcast_filter(f) == 0)) {
ip_mcast_dbg("MCAST: sender is member of group, loopback copy\n");
cpy = pico_frame_copy(f);
- pico_enqueue(&in, cpy);
+ if (!cpy) {
+ pico_err = PICO_ERR_ENOMEM;
+ ip_mcast_dbg("MCAST: Failed to copy frame\n");
+ goto drop;
+ }
+
+ retval = pico_enqueue(&in, cpy);
+ if (retval <= 0)
+ pico_frame_discard(cpy);
}
}
@@ -1064,10 +1122,14 @@ int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t pro
if (pico_ipv4_link_get(&hdr->dst)) {
/* it's our own IP */
- return pico_enqueue(&in, f);
+ retval = pico_enqueue(&in, f);
+ if (retval > 0)
+ return retval;
} else{
/* TODO: Check if there are members subscribed here */
- return pico_enqueue(&out, f);
+ retval = pico_enqueue(&out, f);
+ if (retval > 0)
+ return retval;
}
drop:
@@ -1145,7 +1207,12 @@ int MOCKABLE pico_ipv4_route_add(struct pico_ip4 address, struct pico_ip4 netmas
return -1;
}
- pico_tree_insert(&Routes, new);
+ if (pico_tree_insert(&Routes, new)) {
+ dbg("IPv4: Failed to insert route in tree\n");
+ PICO_FREE(new);
+ return -1;
+ }
+
dbg_route();
return 0;
}
@@ -1222,7 +1289,15 @@ extern int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address,
#endif
#endif
- pico_tree_insert(&Tree_dev_link, new);
+ if (pico_tree_insert(&Tree_dev_link, new)) {
+ dbg("IPv4: Failed to insert link in tree\n");
+#ifdef PICO_SUPPORT_MCAST
+ PICO_FREE(new->MCASTGroups);
+#endif
+ PICO_FREE(new);
+ return -1;
+ }
+
#ifdef PICO_SUPPORT_MCAST
do {
struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm, mcast_gw;
@@ -1243,7 +1318,7 @@ extern int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address,
gateway.addr = 0U;
pico_ipv4_route_add(network, netmask, gateway, 1, new);
pico_ipv4_to_string(ipstr, new->address.addr);
- dbg("Assigned ipv4 %s to device %s\n", ipstr, new->dev->name);
+ DEBUG_EXTRA("Assigned ipv4 %s to device %s", ipstr, new->dev->name);
if (default_bcast_route.link == NULL)
default_bcast_route.link = new;
@@ -1404,7 +1479,7 @@ static int pico_ipv4_rebound_large(struct pico_frame *f)
if (space > PICO_IPV4_MAXPAYLOAD)
space = PICO_IPV4_MAXPAYLOAD;
- fr = pico_ipv4_alloc(&pico_proto_ipv4, (uint16_t)space);
+ fr = pico_ipv4_alloc(&pico_proto_ipv4, NULL, (uint16_t)space);
if (!fr) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -1425,7 +1500,7 @@ static int pico_ipv4_rebound_large(struct pico_frame *f)
if (pico_ipv4_frame_push(fr, &dst, hdr->proto) > 0) {
total_payload_written = (uint16_t)((uint16_t)fr->transport_len + total_payload_written);
} else {
- pico_frame_discard(fr);
+ /* No need to discard frame here, pico_ipv4_frame_push() already did that */
break;
}
} /* while() */
@@ -1539,7 +1614,7 @@ static int pico_ipv4_forward(struct pico_frame *f)
if (pico_ipv4_forward_check_dev(f) < 0)
return -1;
- pico_sendto_dev(f);
+ pico_datalink_send(f);
return 0;
}
diff --git a/ext/picotcp/modules/pico_ipv4.h b/ext/picotcp/modules/pico_ipv4.h
index 095423c..4c98722 100644
--- a/ext/picotcp/modules/pico_ipv4.h
+++ b/ext/picotcp/modules/pico_ipv4.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -10,7 +10,6 @@
#include "pico_addressing.h"
#include "pico_protocol.h"
#include "pico_tree.h"
-#include "pico_device.h"
#define PICO_IPV4_INADDR_ANY 0x00000000U
@@ -68,14 +67,6 @@ struct pico_ipv4_link
#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
{
@@ -88,6 +79,7 @@ struct pico_ipv4_route
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);
@@ -100,11 +92,12 @@ 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);
+int pico_string_to_ipv4(const char *ipstr, uint32_t *ip);
#ifdef __cplusplus
}
#endif
+
int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address);
int pico_ipv4_rebound(struct pico_frame *f);
diff --git a/ext/picotcp/modules/pico_ipv6.c b/ext/picotcp/modules/pico_ipv6.c
index 08bbc48..93ec7b6 100644
--- a/ext/picotcp/modules/pico_ipv6.c
+++ b/ext/picotcp/modules/pico_ipv6.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera, Kristof Roelants
*********************************************************************/
@@ -17,10 +17,13 @@
#include "pico_device.h"
#include "pico_tree.h"
#include "pico_fragments.h"
+#include "pico_ethernet.h"
+#include "pico_6lowpan_ll.h"
#include "pico_mld.h"
-
+#include "pico_mcast.h"
#ifdef PICO_SUPPORT_IPV6
+#include "../../../include/Debug.hpp"
#define PICO_IPV6_EXTHDR_OPT_PAD1 0
#define PICO_IPV6_EXTHDR_OPT_PADN 1
@@ -35,10 +38,22 @@
#define PICO_IPV6_MAX_RTR_SOLICITATION_DELAY 1000
#define PICO_IPV6_DEFAULT_DAD_RETRANS 1
-#define ipv6_dbg(...) do { }while(0);
-#define ipv6_mcast_dbg do{ }while(0);
-static struct pico_ipv6_link *mcast_default_link_ipv6 = NULL;
+#ifdef DEBUG_IPV6
+#define ipv6_dbg dbg
+#else
+#define ipv6_dbg(...) do { } while(0)
+#endif
+#ifdef PICO_SUPPORT_MCAST
+
+#ifdef DEBUG_MCAST
+#define ipv6_mcast_dbg dbg
+#else
+#define ipv6_mcast_dbg(...) do { } while(0)
+#endif
+
+static struct pico_ipv6_link *mcast_default_link_ipv6 = NULL;
+#endif
/* queues */
static struct pico_queue ipv6_in;
static struct pico_queue ipv6_out;
@@ -118,9 +133,9 @@ static int ipv6_route_compare(void *ka, void *kb)
}
-PICO_TREE_DECLARE(Tree_dev_ip6_link, ipv6_link_compare);
+static PICO_TREE_DECLARE(Tree_dev_ip6_link, ipv6_link_compare);
PICO_TREE_DECLARE(IPV6Routes, ipv6_route_compare);
-PICO_TREE_DECLARE(IPV6Links, ipv6_link_compare);
+static PICO_TREE_DECLARE(IPV6Links, ipv6_link_compare);
static char pico_ipv6_dec_to_char(uint8_t u)
{
@@ -314,21 +329,21 @@ int pico_ipv6_is_localhost(const uint8_t addr[PICO_SIZE_IP6])
int pico_ipv6_is_unicast(struct pico_ip6 *a)
{
- if (pico_ipv6_is_global(a->addr))
+ if (pico_ipv6_is_global(a->addr))
return 1;
- else if (pico_ipv6_is_uniquelocal(a->addr))
+ else if (pico_ipv6_is_uniquelocal(a->addr))
return 1;
- else if (pico_ipv6_is_sitelocal(a->addr))
+ else if (pico_ipv6_is_sitelocal(a->addr))
return 1;
- else if (pico_ipv6_is_linklocal(a->addr))
+ else if (pico_ipv6_is_linklocal(a->addr))
return 1;
- else if (pico_ipv6_is_localhost(a->addr))
+ else if (pico_ipv6_is_localhost(a->addr))
return 1;
- else if(pico_ipv6_link_get(a))
+ else if(pico_ipv6_link_get(a))
return 1;
- else
+ else
return 0;
-
+
}
int pico_ipv6_is_multicast(const uint8_t addr[PICO_SIZE_IP6])
@@ -383,14 +398,14 @@ int pico_ipv6_is_unspecified(const uint8_t addr[PICO_SIZE_IP6])
static struct pico_ipv6_route *pico_ipv6_route_find(const struct pico_ip6 *addr)
{
- struct pico_ipv6_route *r = NULL;
struct pico_tree_node *index = NULL;
+ struct pico_ipv6_route *r = NULL;
int i = 0;
if (!pico_ipv6_is_localhost(addr->addr) && (pico_ipv6_is_linklocal(addr->addr) || pico_ipv6_is_sitelocal(addr->addr))) {
return NULL;
}
- pico_tree_foreach_reverse(index, &IPV6Routes)
- {
+
+ pico_tree_foreach_reverse(index, &IPV6Routes) {
r = index->keyValue;
for (i = 0; i < PICO_SIZE_IP6; ++i) {
if ((addr->addr[i] & (r->netmask.addr[i])) != ((r->dest.addr[i]) & (r->netmask.addr[i]))) {
@@ -445,7 +460,7 @@ struct pico_device *pico_ipv6_source_dev_find(const struct pico_ip6 *dst)
static int pico_ipv6_forward_check_dev(struct pico_frame *f)
{
- if(f->dev->eth != NULL)
+ if(f->dev->mode == LL_MODE_ETHERNET && f->dev->eth != NULL)
f->len -= PICO_SIZE_ETHHDR;
if(f->len > f->dev->mtu) {
@@ -467,6 +482,7 @@ static int pico_ipv6_pre_forward_checks(struct pico_frame *f)
dbg(" ------------------- HOP COUNT EXPIRED\n");
return -1;
}
+
/* If source is local, discard anyway (packets bouncing back and forth) */
if (pico_ipv6_link_get(&hdr->src))
return -1;
@@ -503,7 +519,7 @@ static int pico_ipv6_forward(struct pico_frame *f)
f->start = f->net_hdr;
- return pico_sendto_dev(f);
+ return pico_datalink_send(f);
}
@@ -534,9 +550,10 @@ static int pico_ipv6_process_hopbyhop(struct pico_ipv6_exthdr *hbh, struct pico_
break;
case PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT:
optlen = (uint8_t)((*(option + 1)) + 2); /* plus type and len byte */
- // MLD package
- if(*(option+1) == 2)
- must_align = 0;
+ /* MLD package */
+ if(*(option + 1) == 2)
+ must_align = 0;
+
option += optlen;
len = (uint8_t)(len - optlen);
break;
@@ -553,7 +570,7 @@ static int pico_ipv6_process_hopbyhop(struct pico_ipv6_exthdr *hbh, struct pico_
return -1;
case PICO_IPV6_EXTHDR_OPT_ACTION_DISCARD_SINM:
if (!pico_ipv6_is_multicast(((struct pico_ipv6_hdr *)(f->net_hdr))->dst.addr))
- pico_icmp6_parameter_problem(f, PICO_ICMP6_PARAMPROB_IPV6OPT, ptr + (uint32_t)(option - extensions_start));
+ pico_icmp6_parameter_problem(f, PICO_ICMP6_PARAMPROB_IPV6OPT, ptr + (uint32_t)(option - extensions_start));
return -1;
}
@@ -625,9 +642,10 @@ static int pico_ipv6_process_destopt(struct pico_ipv6_exthdr *destopt, struct pi
pico_icmp6_parameter_problem(f, PICO_ICMP6_PARAMPROB_IPV6OPT, opt_ptr);
return -1;
case PICO_IPV6_EXTHDR_OPT_ACTION_DISCARD_SINM:
- if (!pico_ipv6_is_multicast(((struct pico_ipv6_hdr *)(f->net_hdr))->dst.addr)){
- pico_icmp6_parameter_problem(f, PICO_ICMP6_PARAMPROB_IPV6OPT, opt_ptr);
+ if (!pico_ipv6_is_multicast(((struct pico_ipv6_hdr *)(f->net_hdr))->dst.addr)) {
+ pico_icmp6_parameter_problem(f, PICO_ICMP6_PARAMPROB_IPV6OPT, opt_ptr);
}
+
return -1;
}
break;
@@ -639,8 +657,6 @@ static int pico_ipv6_process_destopt(struct pico_ipv6_exthdr *destopt, struct pi
return 0;
}
-#define IPV6_OPTLEN(x) ((uint16_t)(((x + 1) << 3)))
-
static int pico_ipv6_check_headers_sequence(struct pico_frame *f)
{
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
@@ -703,6 +719,7 @@ static int pico_ipv6_extension_headers(struct pico_frame *f)
if (pico_ipv6_check_headers_sequence(f) < 0)
return -1;
+
for (;; ) {
exthdr = (struct pico_ipv6_exthdr *)(f->net_hdr + f->net_len);
cur_optlen = 0;
@@ -781,7 +798,6 @@ static int pico_ipv6_extension_headers(struct pico_frame *f)
return nxthdr;
}
- break;
default:
/* Invalid next header */
pico_icmp6_parameter_problem(f, PICO_ICMP6_PARAMPROB_NXTHDR, cur_nexthdr);
@@ -801,7 +817,8 @@ static int pico_ipv6_process_mcast_in(struct pico_frame *f)
/* Receiving UDP multicast datagram TODO set f->flags? */
if(hdr->nxthdr == 0) {
hbh = (struct pico_ipv6_exthdr *) (f->transport_hdr);
- }
+ }
+
if (hdr->nxthdr == PICO_PROTO_ICMP6 || (hbh != NULL && hbh->nxthdr == PICO_PROTO_ICMP6)) {
pico_transport_receive(f, PICO_PROTO_ICMP6);
return 1;
@@ -810,6 +827,8 @@ static int pico_ipv6_process_mcast_in(struct pico_frame *f)
return 1;
}
+#else
+ IGNORE_PARAMETER(hbh);
#endif
pico_frame_discard(f);
return 1;
@@ -823,11 +842,11 @@ static int pico_ipv6_process_in(struct pico_protocol *self, struct pico_frame *f
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
struct pico_ipv6_exthdr *hbh;
IGNORE_PARAMETER(self);
- /* forward if not local, except if router alert is set */
+ /* forward if not local, except if router alert is set */
if (pico_ipv6_is_unicast(&hdr->dst) && !pico_ipv6_link_get(&hdr->dst)) {
if(hdr->nxthdr == 0) {
hbh = (struct pico_ipv6_exthdr *) f->transport_hdr;
- if(hbh->ext.routing.routtype == 0)
+ if(hbh->ext.routing.routtype == 0)
return pico_ipv6_forward(f);
} else
/* not local, try to forward. */
@@ -849,6 +868,7 @@ static int pico_ipv6_process_in(struct pico_protocol *self, struct pico_frame *f
/* XXX perform multicast filtering: solicited-node multicast address MUST BE allowed! */
if (pico_ipv6_process_mcast_in(f) > 0)
return 0;
+
pico_transport_receive(f, f->proto);
}
@@ -860,7 +880,8 @@ static int pico_ipv6_process_out(struct pico_protocol *self, struct pico_frame *
IGNORE_PARAMETER(self);
f->start = (uint8_t*)f->net_hdr;
- return pico_sendto_dev(f);
+
+ return pico_datalink_send(f);
}
/* allocates an IPv6 packet without extension headers. If extension headers are needed,
@@ -868,22 +889,36 @@ static int pico_ipv6_process_out(struct pico_protocol *self, struct pico_frame *
* increment net_len and transport_hdr with the len of the extension headers, decrement
* transport_len with this value.
*/
-static struct pico_frame *pico_ipv6_alloc(struct pico_protocol *self, uint16_t size)
+static struct pico_frame *pico_ipv6_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size)
{
- struct pico_frame *f = pico_frame_alloc((uint32_t)(size + PICO_SIZE_IP6HDR + PICO_SIZE_ETHHDR));
+ struct pico_frame *f = NULL;
IGNORE_PARAMETER(self);
+ if (0) {}
+#ifdef PICO_SUPPORT_6LOWPAN
+ else if (PICO_DEV_IS_6LOWPAN(dev)) {
+ f = pico_proto_6lowpan_ll.alloc(&pico_proto_6lowpan_ll, dev, (uint16_t)(size + PICO_SIZE_IP6HDR));
+ }
+#endif
+ else {
+#ifdef PICO_SUPPORT_ETH
+ f = pico_proto_ethernet.alloc(&pico_proto_ethernet, dev, (uint16_t)(size + PICO_SIZE_IP6HDR));
+#else
+ f = pico_frame_alloc(size + PICO_SIZE_IP6HDR + PICO_SIZE_ETHHDR);
+#endif
+ }
+
if (!f)
return NULL;
- f->datalink_hdr = f->buffer;
- f->net_hdr = f->buffer + PICO_SIZE_ETHHDR;
f->net_len = PICO_SIZE_IP6HDR;
f->transport_hdr = f->net_hdr + PICO_SIZE_IP6HDR;
f->transport_len = (uint16_t)size;
- /* PICO_SIZE_ETHHDR is accounted for in pico_ethernet_send */
+
+ /* Datalink size is accounted for in pico_datalink_send (link layer) */
f->len = (uint32_t)(size + PICO_SIZE_IP6HDR);
+
return f;
}
@@ -901,9 +936,10 @@ static inline int ipv6_pushed_frame_valid(struct pico_frame *f, struct pico_ip6
return 0;
}
-int pico_ipv6_is_null_address(struct pico_ip6 * ip6) {
- struct pico_ip6 null_addr = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
- return !memcmp(ip6, &null_addr, sizeof(struct pico_ip6));
+int pico_ipv6_is_null_address(struct pico_ip6 *ip6)
+{
+ struct pico_ip6 null_addr = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
+ return !memcmp(ip6, &null_addr, sizeof(struct pico_ip6));
}
#ifdef PICO_SUPPORT_MCAST
/* link
@@ -921,8 +957,8 @@ int pico_ipv6_is_null_address(struct pico_ip6 * ip6) {
*/
static int ipv6_mcast_groups_cmp(void *ka, void *kb)
{
- struct pico_ipv6_mcast_group *a = ka, *b = kb;
- return pico_ipv6_compare(&a->mcast_addr, &b->mcast_addr);
+ struct pico_mcast_group *a = ka, *b = kb;
+ return pico_ipv6_compare(&a->mcast_addr.ip6, &b->mcast_addr.ip6);
}
static int ipv6_mcast_sources_cmp(void *ka, void *kb)
{
@@ -934,7 +970,7 @@ static void pico_ipv6_mcast_print_groups(struct pico_ipv6_link *mcast_link)
{
#ifdef PICO_DEBUG_MULTICAST
uint16_t i = 0;
- struct pico_ipv6_mcast_group *g = NULL;
+ struct pico_mcast_group *g = NULL;
struct pico_ip6 *source = NULL;
struct pico_tree_node *index = NULL, *index2 = NULL;
char *ipv6_addr;
@@ -961,10 +997,10 @@ static void pico_ipv6_mcast_print_groups(struct pico_ipv6_link *mcast_link)
#else
IGNORE_PARAMETER(mcast_link);
#endif
-
+
}
-static int mcast_group_update_ipv6(struct pico_ipv6_mcast_group *g, struct pico_tree *_MCASTFilter, uint8_t filter_mode)
+static int mcast_group_update_ipv6(struct pico_mcast_group *g, struct pico_tree *_MCASTFilter, uint8_t filter_mode)
{
struct pico_tree_node *index = NULL, *_tmp = NULL;
struct pico_ip6 *source = NULL;
@@ -983,20 +1019,22 @@ static int mcast_group_update_ipv6(struct pico_ipv6_mcast_group *g, struct pico_
pico_err = PICO_ERR_ENOMEM;
return -1;
}
-
*source = *((struct pico_ip6 *)index->keyValue);
- pico_tree_insert(&g->MCASTSources, source);
+ if (pico_tree_insert(&g->MCASTSources, source)) {
+ ipv6_mcast_dbg("IPv6 MCAST: Failed to insert source in tree\n");
+ PICO_FREE(source);
+ return -1;
+ }
}
}
}
-
g->filter_mode = filter_mode;
return 0;
}
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)
{
- struct pico_ipv6_mcast_group *g = NULL, test = {
+ struct pico_mcast_group *g = NULL, test = {
0
};
struct pico_ipv6_link *link = NULL;
@@ -1004,42 +1042,56 @@ int pico_ipv6_mcast_join(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_gro
if (mcast_link) {
link = pico_ipv6_link_get(mcast_link);
}
+
if (!link) {
link = mcast_default_link_ipv6;
}
- test.mcast_addr = *mcast_group;
+
+ test.mcast_addr.ip6 = *mcast_group;
g = pico_tree_findKey(link->MCASTGroups, &test);
if (g) {
if (reference_count)
g->reference_count++;
+
+#ifdef PICO_SUPPORT_MLD
res = pico_mld_state_change(mcast_link, mcast_group, filter_mode, _MCASTFilter, PICO_MLD_STATE_UPDATE);
+#endif
} else {
- g = PICO_ZALLOC(sizeof(struct pico_ipv6_mcast_group));
+ g = PICO_ZALLOC(sizeof(struct pico_mcast_group));
if (!g) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
+
/* "non-existent" state of filter mode INCLUDE and empty source list */
g->filter_mode = PICO_IP_MULTICAST_INCLUDE;
g->reference_count = 1;
- g->mcast_addr = *mcast_group;
+ g->mcast_addr.ip6 = *mcast_group;
g->MCASTSources.root = &LEAF;
g->MCASTSources.compare = ipv6_mcast_sources_cmp;
- pico_tree_insert(link->MCASTGroups, g);
+ if (pico_tree_insert(link->MCASTGroups, g)) {
+ ipv6_mcast_dbg("IPv6 MCAST: Failed to insert group in tree\n");
+ PICO_FREE(g);
+ return -1;
+ }
+
+#ifdef PICO_SUPPORT_MLD
res = pico_mld_state_change(mcast_link, mcast_group, filter_mode, _MCASTFilter, PICO_MLD_STATE_CREATE);
+#endif
}
if (mcast_group_update_ipv6(g, _MCASTFilter, filter_mode) < 0) {
dbg("Error in mcast_group update\n");
return -1;
}
+
pico_ipv6_mcast_print_groups(link);
return res;
}
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_mcast_group *g = NULL, test = {
+ struct pico_mcast_group *g = NULL, test = {
0
};
struct pico_ipv6_link *link = NULL;
@@ -1052,14 +1104,16 @@ int pico_ipv6_mcast_leave(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_gr
if (!link)
link = mcast_default_link_ipv6;
- test.mcast_addr = *mcast_group;
+ test.mcast_addr.ip6 = *mcast_group;
g = pico_tree_findKey(link->MCASTGroups, &test);
if (!g) {
pico_err = PICO_ERR_EINVAL;
return -1;
} else {
if (reference_count && (--(g->reference_count) < 1)) {
+#ifdef PICO_SUPPORT_MLD
res = pico_mld_state_change(mcast_link, mcast_group, filter_mode, _MCASTFilter, PICO_MLD_STATE_DELETE);
+#endif
/* cleanup filter */
pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) {
source = index->keyValue;
@@ -1069,7 +1123,9 @@ int pico_ipv6_mcast_leave(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_gr
pico_tree_delete(link->MCASTGroups, g);
PICO_FREE(g);
} else {
+#ifdef PICO_SUPPORT_MLD
res = pico_mld_state_change(mcast_link, mcast_group, filter_mode, _MCASTFilter, PICO_MLD_STATE_UPDATE);
+#endif
if (mcast_group_update_ipv6(g, _MCASTFilter, filter_mode) < 0)
return -1;
}
@@ -1088,14 +1144,14 @@ static int pico_ipv6_mcast_filter(struct pico_frame *f)
{
struct pico_ipv6_link *link = NULL;
struct pico_tree_node *index = NULL, *index2 = NULL;
- struct pico_ipv6_mcast_group *g = NULL, test = {
+ struct pico_mcast_group *g = NULL, test = {
0
};
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *) f->net_hdr;
#ifdef PICO_DEBUG_MULTICAST
char ipv6_addr[PICO_IPV6_STRING];
#endif
- test.mcast_addr = hdr->dst;
+ test.mcast_addr.ip6 = hdr->dst;
pico_tree_foreach(index, &Tree_dev_ip6_link) {
link = index->keyValue;
@@ -1112,32 +1168,32 @@ static int pico_ipv6_mcast_filter(struct pico_frame *f)
pico_tree_foreach(index2, &g->MCASTSources) {
if (hdr->src.addr == ((struct pico_ip6 *)index2->keyValue)->addr) {
#ifdef PICO_DEBUG_MULTICAST
- pico_ipv6_to_string(ipv6_addr,&hdr->src.addr[0]);
+ pico_ipv6_to_string(ipv6_addr, &hdr->src.addr[0]);
ipv6_mcast_dbg("MCAST: IP %s in included interface source list\n", ipv6_addr);
-#endif
- return 0;
+#endif
+ return 0;
}
}
#ifdef PICO_DEBUG_MULTICAST
- pico_ipv6_to_string(ipv6_addr,&hdr->src.addr[0]);
+ pico_ipv6_to_string(ipv6_addr, &hdr->src.addr[0]);
ipv6_mcast_dbg("MCAST: IP %s NOT in included interface source list\n", ipv6_addr);
-#endif
+#endif
return -1;
case PICO_IP_MULTICAST_EXCLUDE:
pico_tree_foreach(index2, &g->MCASTSources) {
- if (memcmp(hdr->src.addr , (((struct pico_ip6 *)index2->keyValue)->addr) , sizeof(struct pico_ip6))== 0){
-#ifdef PICO_DEBUG_MULTICAST
- pico_ipv6_to_string(ipv6_addr,&hdr->src.addr[0]);
+ if (memcmp(hdr->src.addr, (((struct pico_ip6 *)index2->keyValue)->addr), sizeof(struct pico_ip6)) == 0) {
+#ifdef PICO_DEBUG_MULTICAST
+ pico_ipv6_to_string(ipv6_addr, &hdr->src.addr[0]);
ipv6_mcast_dbg("MCAST: IP %s in excluded interface source list\n", ipv6_addr);
-#endif
- return -1;
+#endif
+ return -1;
}
}
#ifdef PICO_DEBUG_MULTICAST
- pico_ipv6_to_string(ipv6_addr,&hdr->src.addr[0]);
+ pico_ipv6_to_string(ipv6_addr, &hdr->src.addr[0]);
ipv6_mcast_dbg("MCAST: IP %s NOT in excluded interface source list\n", ipv6_addr);
-#endif
+#endif
return 0;
default:
@@ -1145,15 +1201,15 @@ static int pico_ipv6_mcast_filter(struct pico_frame *f)
}
} else {
#ifdef PICO_DEBUG_MULTICAST
- pico_ipv6_to_string(ipv6_addr,&hdr->dst.addr[0]);
+ pico_ipv6_to_string(ipv6_addr, &hdr->dst.addr[0]);
ipv6_mcast_dbg("MCAST: IP %s is group member of different link %s\n", ipv6_addr, link->dev->name);
#endif
}
} else {
#ifdef PICO_DEBUG_MULTICAST
- pico_ipv6_to_string(ipv6_addr,&hdr->dst.addr[0]);
+ pico_ipv6_to_string(ipv6_addr, &hdr->dst.addr[0]);
ipv6_mcast_dbg("MCAST: IP %s is not a group member of link %s\n", ipv6_addr, f->dev->name);
-#endif
+#endif
}
}
return -1;
@@ -1163,12 +1219,22 @@ static int pico_ipv6_mcast_filter(struct pico_frame *f)
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)
{
+ IGNORE_PARAMETER(mcast_link);
+ IGNORE_PARAMETER(mcast_group);
+ IGNORE_PARAMETER(reference_count);
+ IGNORE_PARAMETER(filter_mode);
+ IGNORE_PARAMETER(_MCASTFilter);
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
int pico_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)
{
+ IGNORE_PARAMETER(mcast_link);
+ IGNORE_PARAMETER(mcast_group);
+ IGNORE_PARAMETER(reference_count);
+ IGNORE_PARAMETER(filter_mode);
+ IGNORE_PARAMETER(_MCASTFilter);
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
@@ -1234,36 +1300,43 @@ static inline void ipv6_push_hdr_adjust(struct pico_frame *f, struct pico_ipv6_l
/* make adjustments to defaults according to proto */
switch (proto)
{
- case 0:
+#ifdef PICO_SUPPORT_MLD
+ case 0:
{
hbh = (struct pico_ipv6_exthdr *) f->transport_hdr;
switch(hbh->nxthdr) {
- case PICO_PROTO_ICMP6:
- {
- icmp6_hdr = (struct pico_icmp6_hdr *)(f->transport_hdr+sizeof(struct pico_ipv6_exthdr));
- if((icmp6_hdr->type >= PICO_MLD_QUERY && icmp6_hdr->type <= PICO_MLD_DONE) || icmp6_hdr->type == PICO_MLD_REPORTV2) {
- hdr->hop = 1;
- }
- icmp6_hdr->crc = 0;
- icmp6_hdr->crc = short_be(pico_mld_checksum(f));
- break;
+ case PICO_PROTO_ICMP6:
+ {
+ icmp6_hdr = (struct pico_icmp6_hdr *)(f->transport_hdr + sizeof(struct pico_ipv6_exthdr));
+ if((icmp6_hdr->type >= PICO_MLD_QUERY && icmp6_hdr->type <= PICO_MLD_DONE) || icmp6_hdr->type == PICO_MLD_REPORTV2) {
+ hdr->hop = 1;
}
- }
+
+ icmp6_hdr->crc = 0;
+ icmp6_hdr->crc = short_be(pico_mld_checksum(f));
+ break;
+ }
+ }
break;
}
+#else
+ IGNORE_PARAMETER(hbh);
+#endif
case PICO_PROTO_ICMP6:
{
icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
if (icmp6_hdr->type == PICO_ICMP6_NEIGH_SOL || icmp6_hdr->type == PICO_ICMP6_NEIGH_ADV || icmp6_hdr->type == PICO_ICMP6_ROUTER_SOL || icmp6_hdr->type == PICO_ICMP6_ROUTER_ADV)
hdr->hop = 255;
- if ((is_dad || link->istentative) && icmp6_hdr->type == PICO_ICMP6_NEIGH_SOL){
- memcpy(hdr->src.addr, PICO_IP6_ANY, PICO_SIZE_IP6);
-
+ /* RFC6775 $5.5.1:
+ * ... An unspecified source address MUST NOT be used in NS messages.
+ */
+ if (f->dev->mode == LL_MODE_ETHERNET && (is_dad || link->istentative) && icmp6_hdr->type == PICO_ICMP6_NEIGH_SOL) {
+ memcpy(hdr->src.addr, PICO_IP6_ANY, PICO_SIZE_IP6);
}
-
+
icmp6_hdr->crc = 0;
- icmp6_hdr->crc = short_be(pico_icmp6_checksum(f));
+ icmp6_hdr->crc = short_be(pico_icmp6_checksum(f));
break;
}
#ifdef PICO_SUPPORT_UDP
@@ -1300,11 +1373,12 @@ int pico_ipv6_frame_push(struct pico_frame *f, struct pico_ip6 *src, struct pico
struct pico_ipv6_route *route = NULL;
struct pico_ipv6_link *link = NULL;
- if (pico_ipv6_is_linklocal(dst->addr) || pico_ipv6_is_multicast(dst->addr) || pico_ipv6_is_sitelocal(dst->addr)) {
+ if (dst && (pico_ipv6_is_linklocal(dst->addr) || pico_ipv6_is_multicast(dst->addr) || pico_ipv6_is_sitelocal(dst->addr))) {
if (!f->dev) {
pico_frame_discard(f);
return -1;
}
+
if (pico_ipv6_is_sitelocal(dst->addr))
link = pico_ipv6_sitelocal_get(f->dev);
else
@@ -1382,14 +1456,21 @@ struct pico_protocol pico_proto_ipv6 = {
.q_out = &ipv6_out,
};
-#ifdef DEBUG_ROUTE
+#ifdef DEBUG_IPV6_ROUTE
static void pico_ipv6_dbg_route(void)
{
struct pico_ipv6_route *r;
struct pico_tree_node *index;
+ char ipv6_addr[PICO_IPV6_STRING];
+ char netmask_addr[PICO_IPV6_STRING];
+ char gateway_addr[PICO_IPV6_STRING];
+
pico_tree_foreach(index, &Routes){
r = index->keyValue;
- dbg("Route to %08x/%08x, gw %08x, dev: %s, metric: %d\n", r->dest.addr, r->netmask.addr, r->gateway.addr, r->link->dev->name, r->metric);
+ pico_ipv6_to_string(ipv6_addr, r->dest.addr);
+ pico_ipv6_to_string(netmask_addr, r->netmask.addr);
+ pico_ipv6_to_string(gateway_addr, r->gateway.addr);
+ dbg("Route to %s/%s, gw %s, dev: %s, metric: %d\n", ipv6_addr, netmask_addr, gateway_addr, r->link->dev->name, r->metric);
}
}
#else
@@ -1400,7 +1481,8 @@ static inline struct pico_ipv6_route *ipv6_route_add_link(struct pico_ip6 gatewa
{
struct pico_ip6 zerogateway = {{0}};
struct pico_ipv6_route *r = pico_ipv6_route_find(&gateway);
- if (!r ) { /* Specified Gateway is unreachable */
+
+ if (!r) { /* Specified Gateway is unreachable */
pico_err = PICO_ERR_EHOSTUNREACH;
return NULL;
}
@@ -1410,10 +1492,65 @@ static inline struct pico_ipv6_route *ipv6_route_add_link(struct pico_ip6 gatewa
return NULL;
}
-
return r;
}
+struct pico_ipv6_route *pico_ipv6_gateway_by_dev(struct pico_device *dev)
+{
+ struct pico_ipv6_link *link = pico_ipv6_link_by_dev(dev);
+ struct pico_ipv6_route *route = NULL;
+ struct pico_tree_node *node = NULL;
+
+ /* Iterate over the IPv6-routes */
+ pico_tree_foreach(node, &IPV6Routes) {
+ route = (struct pico_ipv6_route *)node->keyValue;
+ /* If the route is a default router, specified by the gw being set */
+ if (!pico_ipv6_is_unspecified(route->gateway.addr) && pico_ipv6_is_unspecified(route->netmask.addr)) {
+ /* Iterate over device's links */
+ while (link) {
+ /* If link is equal to route's link, router list is not empty */
+ if (0 == ipv6_link_compare(link, route->link))
+ return route;
+ link = pico_ipv6_link_by_dev_next(dev, link);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+struct pico_ipv6_route *pico_ipv6_gateway_by_dev_next(struct pico_device *dev, struct pico_ipv6_route *last)
+{
+ struct pico_ipv6_link *link = NULL;
+ struct pico_ipv6_route *gw = NULL;
+ struct pico_tree_node *i = NULL;
+ int valid = 0;
+
+ if (last == NULL)
+ valid = 1;
+
+ pico_tree_foreach(i, &IPV6Routes) {
+ gw = (struct pico_ipv6_route *)i->keyValue;
+ /* If the route is a default router, specified by the gw being set */
+ if (!pico_ipv6_is_unspecified(gw->gateway.addr) && pico_ipv6_is_unspecified(gw->netmask.addr)) {
+ /* Iterate over device's links */
+ link = pico_ipv6_link_by_dev(dev);
+ while (link) {
+ /* If link is equal to route's link, routing list is not empty */
+ if (0 == ipv6_link_compare(link, gw->link)) {
+ if (last == gw) {
+ valid = 1;
+ } else if (valid) {
+ return gw;
+ }
+ link = pico_ipv6_link_by_dev_next(dev, link);
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
int pico_ipv6_route_add(struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link)
{
struct pico_ip6 zerogateway = {{0}};
@@ -1422,6 +1559,7 @@ int pico_ipv6_route_add(struct pico_ip6 address, struct pico_ip6 netmask, struct
test.netmask = netmask;
test.metric = (uint32_t)metric;
if (pico_tree_findKey(&IPV6Routes, &test)) {
+ /* Route already exists */
pico_err = PICO_ERR_EINVAL;
return -1;
}
@@ -1464,8 +1602,12 @@ int pico_ipv6_route_add(struct pico_ip6 address, struct pico_ip6 netmask, struct
return -1;
}
+ if (pico_tree_insert(&IPV6Routes, new)) {
+ ipv6_dbg("IPv6: Failed to insert route in tree\n");
+ PICO_FREE(new);
+ return -1;
+ }
- pico_tree_insert(&IPV6Routes, new);
pico_ipv6_dbg_route();
return 0;
}
@@ -1529,11 +1671,15 @@ static void pico_ipv6_nd_dad(pico_time now, void *arg)
if (pico_device_link_state(l->dev) == 0) {
l->dad_timer = pico_timer_add(100, pico_ipv6_nd_dad, &l->address);
+ if (!l->dad_timer) {
+ dbg("IPv6: Failed to start nd_dad timer\n");
+ /* TODO does this have disastrous consequences? */
+ }
return;
}
if (l->isduplicate) {
- dbg("IPv6: duplicate address.\n");
+ DEBUG_ERROR("IPv6: duplicate address.");
old_address = *address;
if (pico_ipv6_is_linklocal(address->addr)) {
address->addr[8] = (uint8_t)((uint8_t)(pico_rand() & 0xff) & (uint8_t)(~0x03));
@@ -1551,19 +1697,23 @@ static void pico_ipv6_nd_dad(pico_time now, void *arg)
}
else {
if (l->dup_detect_retrans-- == 0) {
- //dbg("IPv6: DAD verified valid address.\n");
+ DEBUG_EXTRA("IPv6: DAD verified valid address.");
+
l->istentative = 0;
} else {
/* Duplicate Address Detection */
- pico_icmp6_neighbor_solicitation(l->dev, &l->address, PICO_ICMP6_ND_DAD);
+ pico_icmp6_neighbor_solicitation(l->dev, &l->address, PICO_ICMP6_ND_DAD, NULL);
l->dad_timer = pico_timer_add(PICO_ICMP6_MAX_RTR_SOL_DELAY, pico_ipv6_nd_dad, &l->address);
+ if (!l->dad_timer) {
+ dbg("IPv6: Failed to start nd_dad timer\n");
+ /* TODO does this have disastrous consequences? */
+ }
}
}
}
#endif
-
-extern struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask)
+static struct pico_ipv6_link *pico_ipv6_do_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask)
{
struct pico_ipv6_link test = {
0
@@ -1572,12 +1722,7 @@ extern struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct
struct pico_ip6 mcast_addr = {{ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
struct pico_ip6 mcast_nm = {{ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
struct pico_ip6 mcast_gw = {{0}};
- struct pico_ip6 all_hosts = {{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }};
-#ifdef PICO_DEBUG_IPV6
- char ipstr[40] = {
- 0
- };
-#endif
+ struct pico_ip6 all_hosts = {{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }};
int i = 0;
if (!dev) {
pico_err = PICO_ERR_EINVAL;
@@ -1618,10 +1763,19 @@ extern struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct
new->MCASTGroups->root = &LEAF;
new->MCASTGroups->compare = ipv6_mcast_groups_cmp;
- new->mcast_compatibility = PICO_MLDV2;
+#ifdef PICO_SUPPORT_MLD
+ new->mcast_compatibility = PICO_MLDV2;
new->mcast_last_query_interval = MLD_QUERY_INTERVAL;
#endif
- pico_tree_insert(&IPV6Links, new);
+#endif
+ if (pico_tree_insert(&IPV6Links, new)) {
+ ipv6_dbg("IPv6: Failed to insert link in tree\n");
+#ifdef PICO_SUPPORT_MCAST
+ PICO_FREE(new->MCASTGroups);
+#endif
+ PICO_FREE(new);
+ return NULL;
+ }
for (i = 0; i < PICO_SIZE_IP6; ++i) {
network.addr[i] = address.addr[i] & netmask.addr[i];
}
@@ -1631,20 +1785,66 @@ extern struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct
mcast_default_link_ipv6 = new;
pico_ipv6_route_add(mcast_addr, mcast_nm, mcast_gw, 1, new);
}
+
pico_ipv6_mcast_join(&address, &all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL);
} while(0);
+#else
+ IGNORE_PARAMETER(all_hosts);
#endif
pico_ipv6_route_add(network, netmask, gateway, 1, new);
- pico_ipv6_route_add(mcast_addr, mcast_nm, mcast_gw, 1, new);
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (!PICO_DEV_IS_6LOWPAN(dev))
+#endif
+ pico_ipv6_route_add(mcast_addr, mcast_nm, mcast_gw, 1, new);
+ /* XXX MUST join the all-nodes multicast address on that interface, as well as
+ * the solicited-node multicast address corresponding to each of the IP
+ * addresses assigned to the interface. (RFC 4861 $7.2.1)
+ * XXX RFC6775 (6LoWPAN): There is no need to join the solicited-node multicast address, since
+ * nobody multicasts NSs in this type of network. A host MUST join the all-nodes multicast
+ * address. */
+#ifdef PICO_DEBUG_IPV6
+ pico_ipv6_to_string(ipstr, new->address.addr);
+ dbg("Assigned ipv6 %s to device %s\n", ipstr, new->dev->name);
+#endif
+ return new;
+}
+
+struct pico_ipv6_link *pico_ipv6_link_add_no_dad(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask)
+{
+ struct pico_ipv6_link *new = pico_ipv6_do_link_add(dev, address, netmask);
+ if (new) {
+ new->istentative = 0;
+ }
+ return new;
+}
+
+extern struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask)
+{
+#ifdef DEBUG_IPV6
+ char ipstr[40] = {
+ 0
+ };
+#endif
+ /* Try to add the basic link */
+ struct pico_ipv6_link *new = pico_ipv6_do_link_add(dev, address, netmask);
+ if (!new)
+ return NULL;
+
+ /* Apply DAD */
new->dup_detect_retrans = PICO_IPV6_DEFAULT_DAD_RETRANS;
#ifndef UNIT_TEST
/* Duplicate Address Detection */
new->dad_timer = pico_timer_add(100, pico_ipv6_nd_dad, &new->address);
+ if (!new->dad_timer) {
+ dbg("IPv6: Failed to start nd_dad timer\n");
+ pico_ipv6_link_del(dev, address);
+ return NULL;
+ }
#else
new->istentative = 0;
#endif
-#ifdef PICO_DEBUG_IPV6
+#ifdef DEBUG_IPV6
pico_ipv6_to_string(ipstr, new->address.addr);
dbg("Assigned ipv6 %s to device %s\n", ipstr, new->dev->name);
#endif
@@ -1735,9 +1935,11 @@ struct pico_ipv6_link *pico_ipv6_link_get(struct pico_ip6 *address)
if (!found) {
return NULL;
}
+
if (found->istentative) {
return NULL;
}
+
return found;
}
@@ -1750,6 +1952,7 @@ struct pico_device *pico_ipv6_link_find(struct pico_ip6 *address)
pico_err = PICO_ERR_EINVAL;
return NULL;
}
+
test.dev = NULL;
test.address = *address;
found = pico_tree_findKey(&IPV6Links, &test);
@@ -1759,7 +1962,7 @@ struct pico_device *pico_ipv6_link_find(struct pico_ip6 *address)
}
if (found->istentative) {
- return NULL;
+ return NULL;
}
return found->dev;
@@ -1856,17 +2059,21 @@ struct pico_ipv6_link *pico_ipv6_global_get(struct pico_device *dev)
{
struct pico_ipv6_link *link = pico_ipv6_link_by_dev(dev);
while (link && !pico_ipv6_is_global(link->address.addr)) {
+ dbg("[0x%02X] - is global: %d - %d\n", link->address.addr[0], pico_ipv6_is_global(link->address.addr), link->address.addr[0] >> 0x05);
link = pico_ipv6_link_by_dev_next(dev, link);
}
return link;
}
-#define TWO_HOURS ((pico_time)(1000 * 60 * 60 * 2))
+#define TWO_HOURS ((pico_time)(1000 * 60 * 60 * 2))
void pico_ipv6_check_lifetime_expired(pico_time now, void *arg)
{
struct pico_tree_node *index = NULL, *temp;
struct pico_ipv6_link *link = NULL;
+#ifdef PICO_SUPPORT_6LOWPAN
+ struct pico_ipv6_route *gw = NULL;
+#endif
(void)arg;
pico_tree_foreach_safe(index, &IPV6Links, temp) {
link = index->keyValue;
@@ -1874,8 +2081,21 @@ void pico_ipv6_check_lifetime_expired(pico_time now, void *arg)
dbg("Warning: IPv6 address has expired.\n");
pico_ipv6_link_del(link->dev, link->address);
}
+#ifdef PICO_SUPPORT_6LOWPAN
+ else if (PICO_DEV_IS_6LOWPAN(link->dev) && !pico_ipv6_is_linklocal(link->address.addr) &&
+ (link->expire_time > 0) && (int)(link->expire_time - now) < (int)(TWO_HOURS >> 4)) {
+ /* RFC6775: The host SHOULD unicast one or more RSs to the router well before the
+ * shortest of the, Router Lifetime, PIO lifetimes and the lifetime of the 6COs. */
+ while ((gw = pico_ipv6_gateway_by_dev_next(link->dev, gw))) {
+ pico_6lp_nd_start_soliciting(link, gw);
+ }
+ }
+#endif
+ }
+ if (!pico_timer_add(1000, pico_ipv6_check_lifetime_expired, NULL)) {
+ dbg("IPv6: Failed to start check_lifetime timer\n");
+ /* TODO No more link lifetime checking now */
}
- pico_timer_add(1000, pico_ipv6_check_lifetime_expired, NULL);
}
int pico_ipv6_lifetime_set(struct pico_ipv6_link *l, pico_time expire)
diff --git a/ext/picotcp/modules/pico_ipv6.h b/ext/picotcp/modules/pico_ipv6.h
index 72064eb..073c49f 100644
--- a/ext/picotcp/modules/pico_ipv6.h
+++ b/ext/picotcp/modules/pico_ipv6.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -10,6 +10,7 @@
#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
@@ -23,11 +24,11 @@
#define PICO_IPV6_EXTHDR_NONE 59
#define PICO_IPV6_EXTHDR_DESTOPT 60
-
#define PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT 5
#define PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT_DATALEN 2
#define HBH_LEN(hbh) ((((hbh->ext.hopbyhop.len + 1) << 3) - 2)) /* len in bytes, minus nxthdr and len byte */
+#define IPV6_OPTLEN(x) ((uint16_t)(((x + 1) << 3)))
extern const uint8_t PICO_IP6_ANY[PICO_SIZE_IP6];
extern struct pico_protocol pico_proto_ipv6;
@@ -60,14 +61,15 @@ struct pico_ipv6_link
uint8_t isduplicate : 1;
uint32_t dad_timer;
uint16_t dup_detect_retrans;
+ uint8_t retrans;
pico_time expire_time;
#ifdef PICO_SUPPORT_MCAST
struct pico_tree *MCASTGroups;
uint8_t mcast_compatibility;
uint8_t mcast_last_query_interval;
#endif
-
};
+
union pico_link {
struct pico_ipv4_link ipv4;
struct pico_ipv6_link ipv6;
@@ -95,6 +97,8 @@ struct pico_ipv6_route
struct pico_ip6 dest;
struct pico_ip6 netmask;
struct pico_ip6 gateway;
+ pico_time backoff;
+ uint8_t retrans;
struct pico_ipv6_link *link;
uint32_t metric;
};
@@ -125,15 +129,6 @@ PACKED_STRUCT_DEF pico_ipv6_exthdr {
} 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);
@@ -153,6 +148,16 @@ int pico_ipv6_route_add(struct pico_ip6 address, struct pico_ip6 netmask, struct
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);
+#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
+
+struct pico_ipv6_link *pico_ipv6_link_add_no_dad(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask);
int pico_ipv6_link_del(struct pico_device *dev, struct pico_ip6 address);
int pico_ipv6_cleanup_links(struct pico_device *dev);
struct pico_ipv6_link *pico_ipv6_link_istentative(struct pico_ip6 *address);
@@ -167,6 +172,8 @@ struct pico_ipv6_link *pico_ipv6_global_get(struct pico_device *dev);
struct pico_ipv6_link *pico_ipv6_linklocal_get(struct pico_device *dev);
struct pico_ipv6_link *pico_ipv6_sitelocal_get(struct pico_device *dev);
struct pico_ipv6_link *pico_ipv6_prefix_configured(struct pico_ip6 *prefix);
+struct pico_ipv6_route *pico_ipv6_gateway_by_dev(struct pico_device *dev);
+struct pico_ipv6_route *pico_ipv6_gateway_by_dev_next(struct pico_device *dev, struct pico_ipv6_route *last);
int pico_ipv6_lifetime_set(struct pico_ipv6_link *l, pico_time expire);
void pico_ipv6_check_lifetime_expired(pico_time now, void *arg);
int pico_ipv6_dev_routing_enable(struct pico_device *dev);
@@ -178,5 +185,5 @@ int pico_ipv6_mcast_leave(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_gr
struct pico_ipv6_link *pico_ipv6_get_default_mcastlink(void);
-int pico_ipv6_is_null_address(struct pico_ip6 * ip6);
+int pico_ipv6_is_null_address(struct pico_ip6 *ip6);
#endif
diff --git a/ext/picotcp/modules/pico_ipv6_nd.c b/ext/picotcp/modules/pico_ipv6_nd.c
index c1048b3..270cabd 100644
--- a/ext/picotcp/modules/pico_ipv6_nd.c
+++ b/ext/picotcp/modules/pico_ipv6_nd.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -16,14 +16,29 @@
#include "pico_eth.h"
#include "pico_addressing.h"
#include "pico_ipv6_nd.h"
+#include "pico_ethernet.h"
+#include "pico_6lowpan.h"
+#include "pico_6lowpan_ll.h"
#ifdef PICO_SUPPORT_IPV6
-
+#ifdef DEBUG_IPV6_ND
+#define nd_dbg dbg
+#else
#define nd_dbg(...) do {} while(0)
+#endif
-static struct pico_frame *frames_queued_v6[PICO_ND_MAX_FRAMES_QUEUED] = { 0 };
+#define ONE_MINUTE ((pico_time)(1000 * 60))
+#ifdef PICO_SUPPORT_6LOWPAN
+ #define MAX_RTR_SOLICITATIONS (3)
+ #define RTR_SOLICITATION_INTERVAL (10000)
+ #define MAX_RTR_SOLICITATION_INTERVAL (60000)
+#endif
+
+static struct pico_frame *frames_queued_v6[PICO_ND_MAX_FRAMES_QUEUED] = {
+ 0
+};
enum pico_ipv6_neighbor_state {
PICO_ND_STATE_INCOMPLETE = 0,
@@ -36,20 +51,29 @@ enum pico_ipv6_neighbor_state {
struct pico_ipv6_neighbor {
enum pico_ipv6_neighbor_state state;
struct pico_ip6 address;
- struct pico_eth mac;
+ union pico_hw_addr hwaddr;
struct pico_device *dev;
uint16_t is_router;
uint16_t failure_count;
pico_time expire;
};
+/******************************************************************************
+ * Function prototypes
+ ******************************************************************************/
+
+#ifdef PICO_SUPPORT_6LOWPAN
+static void pico_6lp_nd_deregister(struct pico_ipv6_link *);
+static void pico_6lp_nd_unreachable_gateway(struct pico_ip6 *a);
+static int pico_6lp_nd_neigh_adv_process(struct pico_frame *f);
+static int neigh_sol_detect_dad_6lp(struct pico_frame *f);
+#endif
+
static int pico_ipv6_neighbor_compare(void *ka, void *kb)
{
struct pico_ipv6_neighbor *a = ka, *b = kb;
return pico_ipv6_compare(&a->address, &b->address);
}
-
-
PICO_TREE_DECLARE(NCache, pico_ipv6_neighbor_compare);
static struct pico_ipv6_neighbor *pico_nd_find_neighbor(struct pico_ip6 *dst)
@@ -70,9 +94,8 @@ static void pico_ipv6_nd_queued_trigger(void)
{
f = frames_queued_v6[i];
if (f) {
- (void)pico_ethernet_send(f);
- if(frames_queued_v6[i])
- pico_frame_discard(frames_queued_v6[i]);
+ if (pico_datalink_send(f) <= 0)
+ pico_frame_discard(f);
frames_queued_v6[i] = NULL;
}
}
@@ -85,22 +108,33 @@ static void ipv6_duplicate_detected(struct pico_ipv6_link *l)
dev = l->dev;
dbg("IPV6: Duplicate address detected. Removing link.\n");
pico_ipv6_link_del(l->dev, l->address);
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (PICO_DEV_IS_6LOWPAN(l->dev)) {
+ pico_6lp_nd_deregister(l);
+ }
+#endif
if (is_ll)
pico_device_ipv6_random_ll(dev);
}
static struct pico_ipv6_neighbor *pico_nd_add(struct pico_ip6 *addr, struct pico_device *dev)
{
- struct pico_ipv6_neighbor *n = PICO_ZALLOC(sizeof(struct pico_ipv6_neighbor));
+ struct pico_ipv6_neighbor *n;
char address[120];
+ /* Create a new NCE */
+ n = PICO_ZALLOC(sizeof(struct pico_ipv6_neighbor));
if (!n)
return NULL;
-
pico_ipv6_to_string(address, addr->addr);
- nd_dbg("Adding address %s to cache...\n", address);
memcpy(&n->address, addr, sizeof(struct pico_ip6));
n->dev = dev;
- pico_tree_insert(&NCache, n);
+
+ if (pico_tree_insert(&NCache, n)) {
+ nd_dbg("IPv6 ND: Failed to insert neigbor in tree\n");
+ PICO_FREE(n);
+ return NULL;
+ }
+
return n;
}
@@ -110,6 +144,11 @@ static void pico_ipv6_nd_unreachable(struct pico_ip6 *a)
struct pico_frame *f;
struct pico_ipv6_hdr *hdr;
struct pico_ip6 dst;
+#ifdef PICO_SUPPORT_6LOWPAN
+ /* 6LP: Find any 6LoWPAN-hosts for which this address might have been a default gateway.
+ * If such a host found, send a router solicitation again */
+ pico_6lp_nd_unreachable_gateway(a);
+#endif /* PICO_SUPPORT_6LOWPAN */
for (i = 0; i < PICO_ND_MAX_FRAMES_QUEUED; i++)
{
f = frames_queued_v6[i];
@@ -145,21 +184,27 @@ static void pico_nd_new_expire_time(struct pico_ipv6_neighbor *n)
static void pico_nd_discover(struct pico_ipv6_neighbor *n)
{
char IPADDR[64];
- if (n->expire != (pico_time)0)
- return;
- pico_ipv6_to_string(IPADDR, n->address.addr);
- /* dbg("Sending NS for %s\n", IPADDR); */
- if (++n->failure_count > PICO_ND_MAX_SOLICIT)
+ if (!n) {
return;
-
- if (n->state == PICO_ND_STATE_INCOMPLETE) {
- pico_icmp6_neighbor_solicitation(n->dev, &n->address, PICO_ICMP6_ND_SOLICITED);
} else {
- pico_icmp6_neighbor_solicitation(n->dev, &n->address, PICO_ICMP6_ND_UNICAST);
- }
+ if (n->expire != (pico_time)0) {
+ return;
+ } else {
+ pico_ipv6_to_string(IPADDR, n->address.addr);
+ /* dbg("Sending NS for %s\n", IPADDR); */
+ if (++n->failure_count > PICO_ND_MAX_SOLICIT)
+ return;
- pico_nd_new_expire_time(n);
+ if (n->state == PICO_ND_STATE_INCOMPLETE) {
+ pico_icmp6_neighbor_solicitation(n->dev, &n->address, PICO_ICMP6_ND_SOLICITED, &n->address);
+ } else {
+ pico_icmp6_neighbor_solicitation(n->dev, &n->address, PICO_ICMP6_ND_UNICAST, &n->address);
+ }
+
+ pico_nd_new_expire_time(n);
+ }
+ }
}
static struct pico_eth *pico_nd_get_neighbor(struct pico_ip6 *addr, struct pico_ipv6_neighbor *n, struct pico_device *dev)
@@ -170,22 +215,19 @@ static struct pico_eth *pico_nd_get_neighbor(struct pico_ip6 *addr, struct pico_
n = pico_nd_add(addr, dev);
pico_nd_discover(n);
return NULL;
+ } else {
+ if (n->state == PICO_ND_STATE_INCOMPLETE) {
+ return NULL;
+ } else if (n->state == PICO_ND_STATE_STALE) {
+ n->state = PICO_ND_STATE_DELAY;
+ pico_nd_new_expire_time(n);
+ }
+
+ if (n->state != PICO_ND_STATE_REACHABLE) {
+ pico_nd_discover(n);
+ }
}
-
- if (n->state == PICO_ND_STATE_INCOMPLETE) {
- return NULL;
- }
-
- if (n->state == PICO_ND_STATE_STALE) {
- n->state = PICO_ND_STATE_DELAY;
- pico_nd_new_expire_time(n);
- }
-
- if (n->state != PICO_ND_STATE_REACHABLE)
- pico_nd_discover(n);
-
- return &n->mac;
-
+ return &n->hwaddr.mac;
}
static struct pico_eth *pico_nd_get(struct pico_ip6 *address, struct pico_device *dev)
@@ -202,29 +244,14 @@ static struct pico_eth *pico_nd_get(struct pico_ip6 *address, struct pico_device
return pico_nd_get_neighbor(&addr, pico_nd_find_neighbor(&addr), dev);
}
-static int neigh_options(struct pico_frame *f, struct pico_icmp6_opt_lladdr *opt, uint8_t expected_opt)
+static int nd_options(uint8_t *options, struct pico_icmp6_opt_lladdr *opt, uint8_t expected_opt, int optlen, int len)
{
- /* RFC 4861 $7.1.2 + $7.2.5.
- * * The contents of any defined options that are not specified to be used
- * * with Neighbor Advertisement messages MUST be ignored and the packet
- * * processed as normal. The only defined option that may appear is the
- * * Target Link-Layer Address option.
- * */
- int optlen = 0;
- uint8_t *option = NULL;
- struct pico_icmp6_hdr *icmp6_hdr = NULL;
- int len;
- uint8_t type;
+ uint8_t type = 0;
int found = 0;
- icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
- optlen = f->transport_len - PICO_ICMP6HDR_NEIGH_ADV_SIZE;
- if (optlen)
- option = ((uint8_t *)&icmp6_hdr->msg.info.neigh_adv) + sizeof(struct neigh_adv_s);
-
while (optlen > 0) {
- type = ((struct pico_icmp6_opt_lladdr *)option)->type;
- len = ((struct pico_icmp6_opt_lladdr *)option)->len;
+ type = ((struct pico_icmp6_opt_lladdr *)options)->type;
+ len = ((struct pico_icmp6_opt_lladdr *)options)->len;
optlen -= len << 3; /* len in units of 8 octets */
if (len <= 0)
return -1; /* malformed option. */
@@ -233,12 +260,12 @@ static int neigh_options(struct pico_frame *f, struct pico_icmp6_opt_lladdr *opt
if (found > 0)
return -1; /* malformed option: option is there twice. */
- memcpy(opt, (struct pico_icmp6_opt_lladdr *)option, (size_t)(len << 3));
+ memcpy(opt, (struct pico_icmp6_opt_lladdr *)options, sizeof(struct pico_icmp6_opt_lladdr));
found++;
}
if (optlen > 0) {
- option += len << 3;
+ options += len << 3;
} else { /* parsing options: terminated. */
return found;
}
@@ -246,14 +273,52 @@ static int neigh_options(struct pico_frame *f, struct pico_icmp6_opt_lladdr *opt
return found;
}
-static void pico_ipv6_neighbor_update(struct pico_ipv6_neighbor *n, struct pico_icmp6_opt_lladdr *opt)
+static int neigh_options(struct pico_frame *f, struct pico_icmp6_opt_lladdr *opt, uint8_t expected_opt)
{
- memcpy(n->mac.addr, opt->addr.mac.addr, PICO_SIZE_ETH);
+ /* RFC 4861 $7.1.2 + $7.2.5.
+ * * The contents of any defined options that are not specified to be used
+ * * with Neighbor Advertisement messages MUST be ignored and the packet
+ * * processed as normal. The only defined option that may appear is the
+ * * Target Link-Layer Address option.
+ * */
+ struct pico_icmp6_hdr *icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
+ uint8_t *option = NULL;
+ int optlen = 0;
+ int len = 0;
+
+ optlen = f->transport_len - PICO_ICMP6HDR_NEIGH_ADV_SIZE;
+ if (optlen)
+ option = ((uint8_t *)&icmp6_hdr->msg.info.neigh_adv) + sizeof(struct neigh_adv_s);
+
+ return nd_options(option, opt, expected_opt, optlen, len);
}
-static int pico_ipv6_neighbor_compare_stored(struct pico_ipv6_neighbor *n, struct pico_icmp6_opt_lladdr *opt)
+static size_t pico_hw_addr_len(struct pico_device *dev, struct pico_icmp6_opt_lladdr *opt)
{
- return memcmp(n->mac.addr, opt->addr.mac.addr, PICO_SIZE_ETH);
+ size_t len = PICO_SIZE_ETH;
+#ifndef PICO_SUPPORT_6LOWPAN
+ IGNORE_PARAMETER(dev);
+ IGNORE_PARAMETER(opt);
+#else
+ if (PICO_DEV_IS_6LOWPAN(dev)) {
+ if (1 == opt->len) {
+ len = (size_t)SIZE_6LOWPAN_SHORT;
+ } else {
+ len = (size_t)SIZE_6LOWPAN_EXT;
+ }
+ }
+#endif
+ return len;
+}
+
+static void pico_ipv6_neighbor_update(struct pico_ipv6_neighbor *n, struct pico_icmp6_opt_lladdr *opt, struct pico_device *dev)
+{
+ memcpy(n->hwaddr.data, opt->addr.data, pico_hw_addr_len(dev, opt));
+}
+
+static int pico_ipv6_neighbor_compare_stored(struct pico_ipv6_neighbor *n, struct pico_icmp6_opt_lladdr *opt, struct pico_device *dev)
+{
+ return memcmp(n->hwaddr.data, opt->addr.data, pico_hw_addr_len(dev, opt));
}
static void neigh_adv_reconfirm_router_option(struct pico_ipv6_neighbor *n, unsigned int isRouter)
@@ -283,10 +348,10 @@ static int neigh_adv_reconfirm_no_tlla(struct pico_ipv6_neighbor *n, struct pico
}
-static int neigh_adv_reconfirm(struct pico_ipv6_neighbor *n, struct pico_icmp6_opt_lladdr *opt, struct pico_icmp6_hdr *hdr)
+static int neigh_adv_reconfirm(struct pico_ipv6_neighbor *n, struct pico_icmp6_opt_lladdr *opt, struct pico_icmp6_hdr *hdr, struct pico_device *dev)
{
- if (IS_SOLICITED(hdr) && !IS_OVERRIDE(hdr) && (pico_ipv6_neighbor_compare_stored(n, opt) == 0)) {
+ if (IS_SOLICITED(hdr) && !IS_OVERRIDE(hdr) && (pico_ipv6_neighbor_compare_stored(n, opt, dev) == 0)) {
n->state = PICO_ND_STATE_REACHABLE;
n->failure_count = 0;
pico_ipv6_nd_queued_trigger();
@@ -300,7 +365,7 @@ static int neigh_adv_reconfirm(struct pico_ipv6_neighbor *n, struct pico_icmp6_o
}
if (IS_SOLICITED(hdr) && IS_OVERRIDE(hdr)) {
- pico_ipv6_neighbor_update(n, opt);
+ pico_ipv6_neighbor_update(n, opt, dev);
n->state = PICO_ND_STATE_REACHABLE;
n->failure_count = 0;
pico_ipv6_nd_queued_trigger();
@@ -308,8 +373,8 @@ static int neigh_adv_reconfirm(struct pico_ipv6_neighbor *n, struct pico_icmp6_o
return 0;
}
- if (!IS_SOLICITED(hdr) && IS_OVERRIDE(hdr) && (pico_ipv6_neighbor_compare_stored(n, opt) != 0)) {
- pico_ipv6_neighbor_update(n, opt);
+ if (!IS_SOLICITED(hdr) && IS_OVERRIDE(hdr) && (pico_ipv6_neighbor_compare_stored(n, opt, dev) != 0)) {
+ pico_ipv6_neighbor_update(n, opt, dev);
n->state = PICO_ND_STATE_STALE;
pico_ipv6_nd_queued_trigger();
pico_nd_new_expire_time(n);
@@ -317,7 +382,7 @@ static int neigh_adv_reconfirm(struct pico_ipv6_neighbor *n, struct pico_icmp6_o
}
if ((n->state == PICO_ND_STATE_REACHABLE) && (!IS_SOLICITED(hdr)) && (!IS_OVERRIDE(hdr)) &&
- (pico_ipv6_neighbor_compare_stored(n, opt) != 0)) {
+ (pico_ipv6_neighbor_compare_stored(n, opt, dev) != 0)) {
/* I. If the Override flag is clear and the supplied link-layer address
* differs from that in the cache, then one of two actions takes
@@ -338,26 +403,26 @@ static int neigh_adv_reconfirm(struct pico_ipv6_neighbor *n, struct pico_icmp6_o
static void neigh_adv_process_incomplete(struct pico_ipv6_neighbor *n, struct pico_frame *f, struct pico_icmp6_opt_lladdr *opt)
{
struct pico_icmp6_hdr *icmp6_hdr = NULL;
- if (!n || !f)
+ if (!n || !f) {
return;
-
- icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
-
- if (!icmp6_hdr)
- return;
-
- if (IS_SOLICITED(icmp6_hdr)) {
- n->state = PICO_ND_STATE_REACHABLE;
- n->failure_count = 0;
- pico_nd_new_expire_time(n);
} else {
- n->state = PICO_ND_STATE_STALE;
+ if (!(icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr))
+ return;
+ else {
+ if (IS_SOLICITED(icmp6_hdr)) {
+ n->state = PICO_ND_STATE_REACHABLE;
+ n->failure_count = 0;
+ pico_nd_new_expire_time(n);
+ } else {
+ n->state = PICO_ND_STATE_STALE;
+ }
+
+ if (opt)
+ pico_ipv6_neighbor_update(n, opt, f->dev);
+
+ pico_ipv6_nd_queued_trigger();
+ }
}
-
- if (opt)
- pico_ipv6_neighbor_update(n, opt);
-
- pico_ipv6_nd_queued_trigger();
}
@@ -375,12 +440,20 @@ static int neigh_adv_process(struct pico_frame *f)
return -1;
}
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (PICO_DEV_IS_6LOWPAN(f->dev)) {
+ /* 6LoWPAN: parse Address Registration Comfirmation(nothing on success, remove link on failure) */
+ pico_6lp_nd_neigh_adv_process(f);
+ }
+#endif
+
+ /* Check if there's a NCE in the cache */
n = pico_nd_find_neighbor(&icmp6_hdr->msg.info.neigh_adv.target);
if (!n) {
return 0;
}
- if ((optres == 0) || IS_OVERRIDE(icmp6_hdr) || (pico_ipv6_neighbor_compare_stored(n, &opt) == 0)) {
+ if ((optres == 0) || IS_OVERRIDE(icmp6_hdr) || (pico_ipv6_neighbor_compare_stored(n, &opt, f->dev) == 0)) {
neigh_adv_reconfirm_router_option(n, IS_ROUTER(icmp6_hdr));
}
@@ -390,22 +463,22 @@ static int neigh_adv_process(struct pico_frame *f)
}
if (optres > 0)
- return neigh_adv_reconfirm(n, &opt, icmp6_hdr);
+ return neigh_adv_reconfirm(n, &opt, icmp6_hdr, f->dev);
else
return neigh_adv_reconfirm_no_tlla(n, icmp6_hdr);
}
-
-
static struct pico_ipv6_neighbor *pico_ipv6_neighbor_from_sol_new(struct pico_ip6 *ip, struct pico_icmp6_opt_lladdr *opt, struct pico_device *dev)
{
+ size_t len = pico_hw_addr_len(dev, opt);
struct pico_ipv6_neighbor *n = NULL;
n = pico_nd_add(ip, dev);
if (!n)
return NULL;
- memcpy(n->mac.addr, opt->addr.mac.addr, PICO_SIZE_ETH);
+ memcpy(n->hwaddr.data, opt->addr.data, len);
+ memset(n->hwaddr.data + len, 0, sizeof(union pico_hw_addr) - len);
n->state = PICO_ND_STATE_STALE;
pico_ipv6_nd_queued_trigger();
return n;
@@ -424,8 +497,8 @@ static void pico_ipv6_neighbor_from_unsolicited(struct pico_frame *f)
n = pico_nd_find_neighbor(&ip->src);
if (!n) {
n = pico_ipv6_neighbor_from_sol_new(&ip->src, &opt, f->dev);
- } else if (memcmp(opt.addr.mac.addr, n->mac.addr, PICO_SIZE_ETH)) {
- pico_ipv6_neighbor_update(n, &opt);
+ } else if (memcmp(opt.addr.data, n->hwaddr.data, pico_hw_addr_len(f->dev, &opt))) {
+ pico_ipv6_neighbor_update(n, &opt, f->dev);
n->state = PICO_ND_STATE_STALE;
pico_ipv6_nd_queued_trigger();
pico_nd_new_expire_time(n);
@@ -438,30 +511,33 @@ static void pico_ipv6_neighbor_from_unsolicited(struct pico_frame *f)
static int neigh_sol_detect_dad(struct pico_frame *f)
{
- struct pico_ipv6_hdr *ipv6_hdr = NULL;
struct pico_icmp6_hdr *icmp6_hdr = NULL;
+ struct pico_ipv6_hdr *ipv6_hdr = NULL;
struct pico_ipv6_link *link = NULL;
ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
- link = pico_ipv6_link_istentative(&icmp6_hdr->msg.info.neigh_adv.target);
- if (link) {
- if (pico_ipv6_is_unicast(&ipv6_hdr->src))
- {
- /* RFC4862 5.4.3 : sender is performing address resolution,
- * our address is not yet valid, discard silently.
- */
- dbg("DAD:Sender performing AR\n");
- }
- else if (pico_ipv6_is_unspecified(ipv6_hdr->src.addr) &&
- !pico_ipv6_is_allhosts_multicast(ipv6_hdr->dst.addr))
- {
- /* RFC4862 5.4.3 : sender is performing DaD */
- dbg("DAD:Sender performing DaD\n");
- ipv6_duplicate_detected(link);
- }
+ if (!f->dev->mode) {
+ link = pico_ipv6_link_istentative(&icmp6_hdr->msg.info.neigh_adv.target);
+ if (link) {
+ if (pico_ipv6_is_unicast(&ipv6_hdr->src))
+ {
+ /* RFC4862 5.4.3 : sender is performing address resolution,
+ * our address is not yet valid, discard silently.
+ */
+ dbg("DAD:Sender performing AR\n");
+ }
- return 0;
+ else if (pico_ipv6_is_unspecified(ipv6_hdr->src.addr) &&
+ !pico_ipv6_is_allhosts_multicast(ipv6_hdr->dst.addr))
+ {
+ /* RFC4862 5.4.3 : sender is performing DaD */
+ dbg("DAD:Sender performing DaD\n");
+ ipv6_duplicate_detected(link);
+ }
+
+ return 0;
+ }
}
return -1; /* Current link is not tentative */
@@ -480,8 +556,14 @@ static int neigh_sol_process(struct pico_frame *f)
valid_lladdr = neigh_options(f, &opt, PICO_ND_OPT_LLADDR_SRC);
pico_ipv6_neighbor_from_unsolicited(f);
- if ((valid_lladdr == 0) && (neigh_sol_detect_dad(f) == 0))
+ if (!f->dev->mode && !valid_lladdr && (0 == neigh_sol_detect_dad(f)))
return 0;
+#ifdef PICO_SUPPORT_6LOWPAN
+ else if (PICO_DEV_IS_6LOWPAN(f->dev)) {
+ nd_dbg("[6LP-ND] Received Address Registration Option\n");
+ neigh_sol_detect_dad_6lp(f);
+ }
+#endif
if (valid_lladdr < 0)
return -1; /* Malformed packet. */
@@ -584,9 +666,26 @@ static int neigh_sol_unicast_validity_check(struct pico_frame *f)
struct pico_ipv6_link *link;
struct pico_icmp6_hdr *icmp6_hdr = NULL;
+#ifdef PICO_SUPPORT_6LOWPAN
+ /* Don't validate target address, the sol is always targeted at 6LBR so
+ * no possible interface on the 6LBR can have the same address as specified in
+ * the target */
+ if (PICO_DEV_IS_6LOWPAN(f->dev))
+ return 0;
+#endif
+
link = pico_ipv6_link_by_dev(f->dev);
icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
while(link) {
+ /* RFC4861, 7.2.3:
+ *
+ * - The Target Address is a "valid" unicast or anycast address
+ * assigned to the receiving interface [ADDRCONF],
+ * - The Target Address is a unicast or anycast address for which the
+ * node is offering proxy service, or
+ * - The Target Address is a "tentative" address on which Duplicate
+ * Address Detection is being performed
+ */
if (pico_ipv6_compare(&link->address, &icmp6_hdr->msg.info.neigh_sol.target) == 0)
return 0;
@@ -613,7 +712,7 @@ static int neigh_sol_validate_unspec(struct pico_frame *f)
0
};
int valid_lladdr = neigh_options(f, &opt, PICO_ND_OPT_LLADDR_SRC);
- if (pico_ipv6_is_solnode_multicast(hdr->dst.addr, f->dev) == 0) {
+ if (!f->dev->mode && pico_ipv6_is_solnode_multicast(hdr->dst.addr, f->dev) == 0) {
return -1;
}
@@ -663,24 +762,424 @@ static int neigh_adv_checks(struct pico_frame *f)
return neigh_adv_validity_checks(f);
}
+/*MARK*/
+#ifdef PICO_SUPPORT_6LOWPAN
+static void pico_6lp_nd_unreachable_gateway(struct pico_ip6 *a)
+{
+ struct pico_ipv6_route *route = NULL;
+ struct pico_ipv6_link *local = NULL;
+ struct pico_tree_node *node = NULL;
+ struct pico_device *dev = NULL;
+
+ /* RFC6775, 5.3:
+ * ... HOSTS need to intelligently retransmit RSs when one of its
+ * default routers becomes unreachable ...
+ */
+ pico_tree_foreach(node, &Device_tree) {
+ if (PICO_DEV_IS_6LOWPAN(dev) && (!dev->hostvars.routing)) {
+ /* Check if there's a gateway configured */
+ route = pico_ipv6_gateway_by_dev(dev);
+ while (route) {
+ if (0 == pico_ipv6_compare(&route->gateway, a)) {
+ local = pico_ipv6_linklocal_get(dev);
+ pico_6lp_nd_start_soliciting(local, route);
+ break;
+ }
+ route = pico_ipv6_gateway_by_dev_next(dev, route);
+ }
+ }
+ }
+}
+
+static int pico_6lp_nd_validate_sol_aro(struct pico_icmp6_opt_aro *aro)
+{
+ if (aro->len != 2 || aro->status != 0)
+ return -1;
+ return 0;
+}
+
+static int pico_6lp_nd_validate_adv_aro(struct pico_device *dev, struct pico_icmp6_opt_aro *aro, uint8_t *status)
+{
+ union pico_ll_addr addr, eui;
+
+ /* RFC6775 - 5.5.2 :
+ * - If the length field is not two, the option is silently ignored.
+ * - If the EUI-64 field does not match the EUI-64 of the interface,
+ * the option is silently ignored.
+ */
+ if (aro->len != 2)
+ return -1;
+
+ /* TODO: Update to abstract address, e.g. remove dependency of '.pan' */
+ eui.pan.addr._ext = aro->eui64;
+ eui.pan.mode = AM_6LOWPAN_EXT;
+ addr.pan.addr._ext = ((struct pico_6lowpan_info *)dev->eth)->addr_ext;
+ addr.pan.mode = AM_6LOWPAN_EXT;
+
+ if (dev && pico_6lowpan_lls[dev->mode].addr_cmp) {
+ if (pico_6lowpan_lls[dev->mode].addr_cmp(&addr, &eui))
+ return -1;
+ } else {
+ return -1;
+ }
+
+ *status = aro->status;
+ return 0;
+}
+
+/* Deregisters a link from all default gateways */
+static void pico_6lp_nd_deregister(struct pico_ipv6_link *l)
+{
+ struct pico_ipv6_route *gw = pico_ipv6_gateway_by_dev(l->dev);
+ while (gw) {
+ pico_icmp6_neighbor_solicitation(l->dev, &l->address, PICO_ICMP6_ND_DEREGISTER, &gw->gateway);
+ gw = pico_ipv6_gateway_by_dev_next(l->dev, gw);
+ }
+}
+
+/* Retransmits neighbors solicitations with address registration if ARO is not acknowledged */
+static void pico_6lp_nd_register_try(pico_time now, void *arg)
+{
+ struct pico_ipv6_link *l = arg;
+ struct pico_ipv6_route *gw = pico_ipv6_gateway_by_dev(l->dev);
+ IGNORE_PARAMETER(now);
+ while (gw) {
+ l->istentative = 1;
+ pico_icmp6_neighbor_solicitation(l->dev, &l->address, PICO_ICMP6_ND_DAD, &gw->gateway);
+ gw = pico_ipv6_gateway_by_dev_next(l->dev, gw);
+ }
+ pico_timer_add(l->dev->hostvars.retranstime, pico_6lp_nd_register_try, l);
+}
+
+/* Tries to register a link with one or more of its default routers */
+void pico_6lp_nd_register(struct pico_ipv6_link *link)
+{
+ /* RFC6775: When a host has configured a non-link-local IPv6 address, it registers that
+ * address with one or more of its default routers using the Address Registration
+ * Option (ARO) in an NS message. */
+ pico_6lp_nd_register_try(PICO_TIME_MS(), link);
+}
+
+/* Check if there are default routers configured. If not, sent a router solicitation */
+static void pico_6lp_nd_do_solicit(pico_time now, void *arg)
+{
+ struct pico_ipv6_route *gw = arg;
+ struct pico_ip6 *dst = NULL;
+ IGNORE_PARAMETER(now);
+
+ if (!pico_ipv6_gateway_by_dev(gw->link->dev) && !gw->link->dev->hostvars.routing) {
+ /* If the solicitation is to be sent unicast */
+ if (!pico_ipv6_is_unspecified(gw->gateway.addr) && gw->retrans < MAX_RTR_SOLICITATIONS)
+ dst = &gw->gateway;
+
+ /* Exponential backoff */
+ if (++gw->retrans == MAX_RTR_SOLICITATIONS) {
+ gw->backoff <<= 1;
+ if (gw->backoff >= MAX_RTR_SOLICITATION_INTERVAL)
+ gw->backoff = (pico_time)MAX_RTR_SOLICITATION_INTERVAL;
+ }
+
+ /* If router list is empty, send router solicitation */
+ pico_icmp6_router_solicitation(gw->link->dev, &gw->link->address, dst);
+
+ /* Apply exponential retransmission timer, see RFC6775 5.3 */
+ pico_timer_add(gw->backoff, pico_6lp_nd_do_solicit, gw);
+ nd_dbg("[6LP-ND]$ No default routers configured, soliciting\n");
+ } else {
+ PICO_FREE(gw);
+ }
+}
+
+/* Start transmitting repetitive router solicitations */
+int pico_6lp_nd_start_soliciting(struct pico_ipv6_link *l, struct pico_ipv6_route *gw)
+{
+ struct pico_ipv6_route *dummy = PICO_ZALLOC(sizeof(struct pico_ipv6_route));
+ struct pico_ip6 *dst = NULL;
+
+ if (dummy) {
+ if (gw) { // If the router solicitation has to be sent unicast ...
+ dst = &gw->gateway; // ... the gateway is the destination
+ memcpy(dummy->gateway.addr, gw->gateway.addr, PICO_SIZE_IP6); // and should be retrievable in the timer event
+ }
+ dummy->link = l; // the link that has to be reconfirmed as well.
+
+ /* If router list is empty, send router solicitation */
+ pico_icmp6_router_solicitation(l->dev, &l->address, dst);
+
+ if (!l->dev->hostvars.routing) {
+ dummy->retrans = 0;
+ dummy->backoff = RTR_SOLICITATION_INTERVAL;
+ if (!pico_timer_add(dummy->backoff, pico_6lp_nd_do_solicit, dummy)) {
+ PICO_FREE(dummy);
+ return -1;
+ }
+ } else {
+ PICO_FREE(dummy);
+ }
+ return 0;
+ }
+ return -1;
+}
+
+/* Validate Neighbor advertisement mesaage */
+static int pico_6lp_nd_neigh_adv_validate(struct pico_frame *f, uint8_t *status)
+{
+ struct pico_icmp6_hdr *icmp = (struct pico_icmp6_hdr *)f->transport_hdr;
+ struct pico_icmp6_opt_aro *aro = (struct pico_icmp6_opt_aro *)((uint8_t *)&icmp->msg.info.neigh_adv + sizeof(struct neigh_sol_s));
+ struct pico_ipv6_hdr *ip = (struct pico_ipv6_hdr *)f->net_hdr;
+
+ /* 6LP: Target address cannot be MCAST and the Source IP-address cannot be UNSPECIFIED or MCAST */
+ if (pico_ipv6_is_multicast(icmp->msg.info.neigh_adv.target.addr) || pico_ipv6_is_unspecified(ip->src.addr) ||
+ pico_ipv6_is_multicast(ip->src.addr))
+ return -1;
+
+ return pico_6lp_nd_validate_adv_aro(f->dev, aro, status);
+}
+
+/* Process neighbor advertisement */
+static int pico_6lp_nd_neigh_adv_process(struct pico_frame *f)
+{
+ struct pico_icmp6_hdr *icmp = (struct pico_icmp6_hdr *)f->transport_hdr;
+ struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
+ struct pico_ipv6_link *l = NULL;
+ struct pico_ip6 zero = {
+ .addr = {0}
+ };
+ uint8_t status = 0;
+
+ if (pico_6lp_nd_neigh_adv_validate(f, &status)) {
+ return -1;
+ } else {
+ l = pico_ipv6_link_get(&icmp->msg.info.neigh_adv.target);
+ if (l)
+ l->istentative = 0;
+ else
+ return -1;
+
+ /* Globally routable address has been registered @ 6LoWPAN Border Router */
+ if (1 == status) { // Duplicate address detected
+ nd_dbg("[6LP-ND]: Registering routable address failed, removing link...\n");
+ ipv6_duplicate_detected(l);
+ return -1;
+ } else if (2 == status) { // Router's NCE is full, remove router from default router list
+ pico_ipv6_route_del(zero, zero, hdr->src, 10, l);
+ pico_6lp_nd_start_soliciting(pico_ipv6_linklocal_get(l->dev), NULL);
+ } else { // Registration success
+ nd_dbg("[6LP-ND]: Registering routable address succeeded!\n");
+ }
+ }
+ return 0;
+}
+
+/* Add a new 6LoWPAN neighbor with lifetime from ARO */
+static struct pico_ipv6_neighbor *pico_nd_add_6lp(struct pico_ip6 naddr, struct pico_icmp6_opt_aro *aro, struct pico_device *dev)
+{
+ struct pico_ipv6_neighbor *new = NULL;
+
+ if ((new = pico_nd_add(&naddr, dev))) {
+ new->expire = PICO_TIME_MS() + (pico_time)(ONE_MINUTE * aro->lifetime);
+ dbg("ARO Lifetime: %d minutes\n", aro->lifetime);
+ } else {
+ return NULL;
+ }
+
+ return new;
+}
+
+/* RFC6775 §6.5.2. Returning Address Registration Errors */
+static int neigh_sol_dad_reply(struct pico_frame *sol, struct pico_icmp6_opt_lladdr *sllao, struct pico_icmp6_opt_aro *aro, uint8_t status)
+{
+ uint8_t sllao_len = (uint8_t)(sllao->len * 8);
+ struct pico_icmp6_hdr *icmp = NULL;
+ struct pico_frame *adv = pico_frame_copy(sol);
+ struct pico_ip6 ll = {{0xfe,0x80,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}};
+ size_t len = pico_hw_addr_len(sol->dev, sllao);
+ union pico_ll_addr lladdr;
+
+ if (!adv) {
+ return -1;
+ } else {
+ icmp = (struct pico_icmp6_hdr *)adv->transport_hdr;
+
+ /* Set the status of the Address Registration */
+ aro->status = status;
+ if (PICO_DEV_IS_6LOWPAN(sol->dev)) {
+ memcpy(lladdr.pan.addr.data, aro->eui64.addr, len);
+ lladdr.pan.mode = (len == SIZE_6LOWPAN_EXT) ? AM_6LOWPAN_EXT : AM_6LOWPAN_SHORT;
+ if (pico_6lowpan_lls[sol->dev->mode].addr_iid)
+ pico_6lowpan_lls[sol->dev->mode].addr_iid(ll.addr + 8, &lladdr);
+ }
+
+ /* Remove the SLLAO from the frame */
+ memmove(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s), ((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s) + sllao_len, (size_t)(aro->len * 8));
+ adv->transport_len = (uint16_t)(adv->transport_len - sllao_len);
+ adv->len = (uint16_t)(adv->len - sllao_len);
+
+ /* I'm a router, and it's always solicited */
+ icmp->msg.info.neigh_adv.rsor = 0xE0;
+
+ /* Set the ICMPv6 message type to Neighbor Advertisements */
+ icmp->type = PICO_ICMP6_NEIGH_ADV;
+ icmp->code = 0;
+ icmp->crc = pico_icmp6_checksum(adv);
+
+ pico_ipv6_frame_push(adv, NULL, &ll, PICO_PROTO_ICMP6, 0);
+ return 0;
+ }
+}
+
+/* RFC6775 §6.5.1. Checking for Duplicates */
+static int neigh_sol_detect_dad_6lp(struct pico_frame *f)
+{
+ struct pico_ipv6_neighbor *n = NULL;
+ struct pico_icmp6_opt_lladdr *sllao = NULL;
+ struct pico_icmp6_hdr *icmp = NULL;
+ struct pico_icmp6_opt_aro *aro = NULL;
+ size_t len = 0;
+
+ icmp = (struct pico_icmp6_hdr *)f->transport_hdr;
+ sllao = (struct pico_icmp6_opt_lladdr *)((uint8_t *)&icmp->msg.info.neigh_sol + sizeof(struct neigh_sol_s));
+ aro = (struct pico_icmp6_opt_aro *)(((uint8_t *)&icmp->msg.info.neigh_sol) + sizeof(struct neigh_sol_s) + (sllao->len * 8));
+
+ /* Validate Address Registration Option */
+ if (pico_6lp_nd_validate_sol_aro(aro))
+ return -1;
+
+ /* See RFC6775 $6.5.1: Checking for duplicates */
+ if (!(n = pico_nd_find_neighbor(&icmp->msg.info.neigh_sol.target))) {
+ /* No dup, add neighbor to cache */
+ if (pico_nd_add_6lp(icmp->msg.info.neigh_sol.target, aro, f->dev))
+ neigh_sol_dad_reply(f, sllao, aro, ICMP6_ARO_SUCCES);
+ else /* No dup, but neighbor cache is full */
+ neigh_sol_dad_reply(f, sllao, aro, ICMP6_ARO_FULL);
+ return 0;
+ } else {
+ if (!aro->lifetime) {
+ pico_tree_delete(&NCache, n);
+ PICO_FREE(n);
+ neigh_sol_dad_reply(f, sllao, aro, ICMP6_ARO_SUCCES);
+ return 0;
+ }
+ /* Check if hwaddr differs */
+ len = pico_hw_addr_len(f->dev, sllao);
+ if (memcmp(sllao->addr.data, n->hwaddr.data, len) == 0) {
+ n->expire = PICO_TIME_MS() + (pico_time)(ONE_MINUTE * aro->lifetime);
+ neigh_sol_dad_reply(f, sllao, aro, ICMP6_ARO_DUP);
+ }
+ return 0;
+ }
+}
+
+static int router_options(struct pico_frame *f, struct pico_icmp6_opt_lladdr *opt, uint8_t expected_opt)
+{
+ /* RFC 4861 $6.1
+ * The contents of any defined options that are not specified to be used
+ * with Router Solicitation messages MUST be ignored and the packet
+ * processed as normal. The only defined option that may appear is the
+ * Source Link-Layer Address option.
+ */
+ struct pico_icmp6_hdr *icmp6_hdr = NULL;
+ uint8_t *options = NULL;
+ int optlen = 0;
+ int len = 0;
+
+ icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
+ optlen = f->transport_len - PICO_ICMP6HDR_ROUTER_SOL_SIZE;
+ if (optlen)
+ options = ((uint8_t *)&icmp6_hdr->msg.info.router_sol) + sizeof(struct router_sol_s);
+
+ return nd_options(options, opt, expected_opt, optlen, len);
+}
+
+static int router_sol_validity_checks(struct pico_frame *f)
+{
+ struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
+ struct pico_icmp6_opt_lladdr opt = { 0 };
+ int sllao_present = 0;
+
+ /* Step 2 validation */
+ if (f->transport_len < PICO_ICMP6HDR_ROUTER_SOL_SIZE_6LP)
+ return -1;
+
+ /* RFC4861, 6.1.1:
+ * - If the IP source address is the unspecified address, there is no
+ * source link-layer address option in the message.
+ */
+ /* Check for SLLAO if the IP source address is UNSPECIFIED */
+ sllao_present = router_options(f, &opt, PICO_ND_OPT_LLADDR_SRC);
+ if (pico_ipv6_is_unspecified(hdr->src.addr)) {
+ /* Frame is not valid when SLLAO is present if IP6-SRC is UNSPEC. */
+ if (sllao_present) {
+ return -1;
+ }
+ } else {
+ /* Frame is not valid when no SLLAO if present if there's a IP6-SRC */
+ if (sllao_present <= 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int router_sol_checks(struct pico_frame *f)
+{
+ /* Step 1 validation */
+ if (icmp6_initial_checks(f) < 0)
+ return -1;
+
+ return router_sol_validity_checks(f);
+}
+
+static int router_sol_process(struct pico_frame *f)
+{
+ struct pico_ipv6_hdr *hdr = NULL;
+
+ /* Determine if i'm a 6LBR, if i'm not, can't do anything with a router solicitation */
+ if (!f->dev->hostvars.routing)
+ return -1;
+
+ nd_dbg("[6LBR]: Processing router solicitation...\n");
+
+ /* Router solicitation message validation */
+ if (router_sol_checks(f) < 0)
+ return -1;
+
+ /* Maybe create a tentative NCE? No, will do it later */
+
+ /* Send a router advertisement via unicast to requesting host */
+ hdr = (struct pico_ipv6_hdr *)f->net_hdr;
+ return pico_icmp6_router_advertisement(f->dev, &hdr->src);
+}
+
+#endif /* PICO_SUPPORT_6LOWPAN */
static int pico_nd_router_sol_recv(struct pico_frame *f)
{
+#ifdef PICO_SUPPORT_6LOWPAN
+ /* 6LoWPAN: reply on explicit router solicitations via unicast */
+ if (PICO_DEV_IS_6LOWPAN(f->dev))
+ return router_sol_process(f);
+#endif
+
pico_ipv6_neighbor_from_unsolicited(f);
/* Host only: router solicitation is discarded. */
return 0;
}
-
static int radv_process(struct pico_frame *f)
{
struct pico_icmp6_hdr *icmp6_hdr = NULL;
uint8_t *nxtopt, *opt_start;
struct pico_ipv6_link *link;
+ uint32_t pref_lifetime = 0;
struct pico_ipv6_hdr *hdr;
struct pico_ip6 zero = {
.addr = {0}
};
int optlen;
+#ifdef PICO_SUPPORT_6LOWPAN
+ int sllao = 0;
+#endif
hdr = (struct pico_ipv6_hdr *)f->net_hdr;
icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
@@ -712,27 +1211,43 @@ static int radv_process(struct pico_frame *f)
/* c) If the preferred lifetime is greater than the valid lifetime,
* silently ignore the Prefix Information option
*/
- if (long_be(prefix->pref_lifetime) > long_be(prefix->val_lifetime))
+ pref_lifetime = long_be(prefix->pref_lifetime);
+ if (pref_lifetime > long_be(prefix->val_lifetime))
goto ignore_opt_prefix;
+#ifdef PICO_SUPPORT_6LOWPAN
+ /* RFC6775 (6LoWPAN): Should the host erroneously receive a PIO with the L (on-link)
+ * flag set, then that PIO MUST be ignored.
+ */
+ if (PICO_DEV_IS_6LOWPAN(f->dev) && prefix->onlink)
+ goto ignore_opt_prefix;
+#endif
+
if (prefix->val_lifetime == 0)
goto ignore_opt_prefix;
-
if (prefix->prefix_len != 64) {
return -1;
}
+ /* Refresh lifetime of a prefix */
link = pico_ipv6_prefix_configured(&prefix->prefix);
if (link) {
- pico_ipv6_lifetime_set(link, now + (pico_time)(1000 * (long_be(prefix->val_lifetime))));
+ pico_ipv6_lifetime_set(link, now + (1000 * (pico_time)(long_be(prefix->val_lifetime))));
goto ignore_opt_prefix;
}
+ /* Configure a an non linklocal IPv6 address */
link = pico_ipv6_link_add_local(f->dev, &prefix->prefix);
if (link) {
- pico_ipv6_lifetime_set(link, now + (pico_time)(1000 * (long_be(prefix->val_lifetime))));
+ pico_ipv6_lifetime_set(link, now + (1000 * (pico_time)(long_be(prefix->val_lifetime))));
+ /* Add a default gateway to the default routers list with source of RADV */
pico_ipv6_route_add(zero, zero, hdr->src, 10, link);
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (PICO_DEV_IS_6LOWPAN(f->dev)) {
+ pico_6lp_nd_register(link);
+ }
+#endif
}
ignore_opt_prefix:
@@ -744,6 +1259,9 @@ ignore_opt_prefix:
{
struct pico_icmp6_opt_lladdr *lladdr_src =
(struct pico_icmp6_opt_lladdr *) nxtopt;
+#ifdef PICO_SUPPORT_6LOWPAN
+ sllao = 1; // RFC6775 (6LoWPAN): An SLLAO MUST be included in the RA.
+#endif
optlen -= (lladdr_src->len << 3);
nxtopt += (lladdr_src->len << 3);
}
@@ -776,12 +1294,41 @@ ignore_opt_prefix:
nxtopt += (rdnss->len << 3);
}
break;
+#ifdef PICO_SUPPORT_6LOWPAN
+ case PICO_ND_OPT_6CO:
+ {
+ struct pico_icmp6_opt_6co *co = (struct pico_icmp6_opt_6co *)nxtopt;
+#ifdef PICO_6LOWPAN_IPHC_ENABLED
+ if (PICO_DEV_IS_6LOWPAN(f->dev)) {
+ struct pico_ip6 prefix;
+ memcpy(prefix.addr, (uint8_t *)&co->prefix, (size_t)(co->len - 1) << 3);
+ ctx_update(prefix, co->id, co->clen, co->lifetime, co->c, f->dev);
+ }
+#endif
+ optlen -= (co->len << 3);
+ nxtopt += (co->len << 3);
+ }
+ break;
+ case PICO_ND_OPT_ABRO:
+ {
+ struct pico_icmp6_opt_abro *abro = (struct pico_icmp6_opt_abro *)nxtopt;
+ /* TODO: Process */
+ optlen -= (abro->len << 3);
+ nxtopt += (abro->len << 3);
+ }
+ break;
+#endif
default:
pico_icmp6_parameter_problem(f, PICO_ICMP6_PARAMPROB_IPV6OPT,
(uint32_t)sizeof(struct pico_ipv6_hdr) + (uint32_t)PICO_ICMP6HDR_ROUTER_ADV_SIZE + (uint32_t)(nxtopt - opt_start));
return -1;
}
}
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (PICO_DEV_IS_6LOWPAN(f->dev) && !sllao) {
+ return -1;
+ }
+#endif
if (icmp6_hdr->msg.info.router_adv.retrans_time != 0u) {
f->dev->hostvars.retranstime = long_be(icmp6_hdr->msg.info.router_adv.retrans_time);
}
@@ -823,8 +1370,9 @@ static int pico_nd_neigh_adv_recv(struct pico_frame *f)
return -1;
}
+ /* ETH: Target address belongs to a tentative link on this device, DaD detected a dup */
link = pico_ipv6_link_istentative(&icmp6_hdr->msg.info.neigh_adv.target);
- if (link)
+ if (link && !link->dev->mode)
ipv6_duplicate_detected(link);
return neigh_adv_process(f);
@@ -882,11 +1430,14 @@ static void pico_ipv6_nd_timer_callback(pico_time now, void *arg)
pico_tree_foreach_safe(index, &NCache, _tmp)
{
n = index->keyValue;
- if ( now > n->expire) {
+ if ( now > n->expire ) {
pico_ipv6_nd_timer_elapsed(now, n);
}
}
- pico_timer_add(200, pico_ipv6_nd_timer_callback, NULL);
+ if (!pico_timer_add(200, pico_ipv6_nd_timer_callback, NULL)) {
+ dbg("IPV6 ND: Failed to start callback timer\n");
+ /* TODO no idea what consequences this has */
+ }
}
#define PICO_IPV6_ND_MIN_RADV_INTERVAL (5000)
@@ -909,14 +1460,20 @@ static void pico_ipv6_nd_ra_timer_callback(pico_time now, void *arg)
if (pico_ipv6_compare(&nm64, &rt->netmask) == 0) {
pico_tree_foreach(devindex, &Device_tree) {
dev = devindex->keyValue;
- if ((!pico_ipv6_is_linklocal(rt->dest.addr)) && dev->hostvars.routing && (rt->link) && (dev != rt->link->dev)) {
+ /* Do not send periodic router advertisements when there aren't 2 interfaces from and to the device can route */
+ if ((!pico_ipv6_is_linklocal(rt->dest.addr)) && dev->hostvars.routing && (rt->link)
+ && (dev != rt->link->dev) && !PICO_DEV_IS_6LOWPAN(dev)) {
pico_icmp6_router_advertisement(dev, &rt->dest);
}
}
}
}
+
next_timer_expire = PICO_IPV6_ND_MIN_RADV_INTERVAL + (pico_rand() % (PICO_IPV6_ND_MAX_RADV_INTERVAL - PICO_IPV6_ND_MIN_RADV_INTERVAL));
- pico_timer_add(next_timer_expire, pico_ipv6_nd_ra_timer_callback, NULL);
+ if (!pico_timer_add(next_timer_expire, pico_ipv6_nd_ra_timer_callback, NULL)) {
+ dbg("IPv6 ND: Failed to start callback timer\n");
+ /* TODO no idea what consequences this has */
+ }
}
/* Public API */
@@ -935,8 +1492,10 @@ struct pico_eth *pico_ipv6_get_neighbor(struct pico_frame *f)
/* address belongs to ourselves? */
l = pico_ipv6_link_get(&hdr->dst);
- if (l)
+ if (l && !l->dev->mode)
return &l->dev->eth->mac;
+ else if (l && PICO_DEV_IS_6LOWPAN(l->dev))
+ return (struct pico_eth *)l->dev->eth;
return pico_nd_get(&hdr->dst, f->dev);
}
@@ -945,11 +1504,10 @@ void pico_ipv6_nd_postpone(struct pico_frame *f)
{
int i;
static int last_enq = -1;
- struct pico_frame *cp = pico_frame_copy(f);
for (i = 0; i < PICO_ND_MAX_FRAMES_QUEUED; i++)
{
if (!frames_queued_v6[i]) {
- frames_queued_v6[i] = cp;
+ frames_queued_v6[i] = f;
last_enq = i;
return;
}
@@ -962,7 +1520,7 @@ void pico_ipv6_nd_postpone(struct pico_frame *f)
if (frames_queued_v6[last_enq])
pico_frame_discard(frames_queued_v6[last_enq]);
- frames_queued_v6[last_enq] = cp;
+ frames_queued_v6[last_enq] = f;
}
@@ -978,6 +1536,7 @@ int pico_ipv6_nd_recv(struct pico_frame *f)
break;
case PICO_ICMP6_ROUTER_ADV:
+ nd_dbg("ICMP6: received ROUTER ADV\n");
ret = pico_nd_router_adv_recv(f);
break;
@@ -1001,9 +1560,27 @@ int pico_ipv6_nd_recv(struct pico_frame *f)
void pico_ipv6_nd_init(void)
{
- pico_timer_add(200, pico_ipv6_nd_timer_callback, NULL);
- pico_timer_add(200, pico_ipv6_nd_ra_timer_callback, NULL);
- pico_timer_add(1000, pico_ipv6_check_lifetime_expired, NULL);
+ uint32_t timer_cb = 0, ra_timer_cb = 0;
+
+ timer_cb = pico_timer_add(200, pico_ipv6_nd_timer_callback, NULL);
+ if (!timer_cb) {
+ nd_dbg("IPv6 ND: Failed to start callback timer\n");
+ return;
+ }
+
+ ra_timer_cb = pico_timer_add(200, pico_ipv6_nd_ra_timer_callback, NULL);
+ if (!ra_timer_cb) {
+ nd_dbg("IPv6 ND: Failed to start RA callback timer\n");
+ pico_timer_cancel(timer_cb);
+ return;
+ }
+
+ if (!pico_timer_add(1000, pico_ipv6_check_lifetime_expired, NULL)) {
+ nd_dbg("IPv6 ND: Failed to start check_lifetime timer\n");
+ pico_timer_cancel(timer_cb);
+ pico_timer_cancel(ra_timer_cb);
+ return;
+ }
}
#endif
diff --git a/ext/picotcp/modules/pico_ipv6_nd.h b/ext/picotcp/modules/pico_ipv6_nd.h
index f709b1e..792b229 100644
--- a/ext/picotcp/modules/pico_ipv6_nd.h
+++ b/ext/picotcp/modules/pico_ipv6_nd.h
@@ -1,11 +1,12 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef _INCLUDE_PICO_ND
#define _INCLUDE_PICO_ND
#include "pico_frame.h"
+#include "pico_ipv6.h"
/* RFC constants */
#define PICO_ND_REACHABLE_TIME 30000 /* msec */
@@ -17,10 +18,19 @@ struct pico_nd_hostvars {
pico_time basetime;
pico_time reachabletime;
pico_time retranstime;
+#ifdef PICO_SUPPORT_6LOWPAN
+ uint8_t lowpan_flags;
+#endif
};
void pico_ipv6_nd_init(void);
struct pico_eth *pico_ipv6_get_neighbor(struct pico_frame *f);
void pico_ipv6_nd_postpone(struct pico_frame *f);
int pico_ipv6_nd_recv(struct pico_frame *f);
+
+#ifdef PICO_SUPPORT_6LOWPAN
+int pico_6lp_nd_start_soliciting(struct pico_ipv6_link *l, struct pico_ipv6_route *gw);
+void pico_6lp_nd_register(struct pico_ipv6_link *link);
+#endif
+
#endif
diff --git a/ext/picotcp/modules/pico_mcast.c b/ext/picotcp/modules/pico_mcast.c
new file mode 100644
index 0000000..74b4a01
--- /dev/null
+++ b/ext/picotcp/modules/pico_mcast.c
@@ -0,0 +1,259 @@
+/*********************************************************************
+ PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
+
+ This module handles the equalities between the IGMP and the MLD protocol
+ Authors: Roel Postelmans
+ *********************************************************************/
+
+#include "pico_stack.h"
+#include "pico_ipv6.h"
+#include "pico_mld.h"
+#include "pico_config.h"
+#include "pico_eth.h"
+#include "pico_addressing.h"
+#include "pico_frame.h"
+#include "pico_tree.h"
+#include "pico_device.h"
+#include "pico_socket.h"
+#include "pico_icmp6.h"
+#include "pico_dns_client.h"
+#include "pico_mld.h"
+#include "pico_igmp.h"
+#include "pico_constants.h"
+#include "pico_mcast.h"
+
+#if (((defined(PICO_SUPPORT_MLD) && defined(PICO_SUPPORT_IPV6)) || defined(PICO_SUPPORT_IGMP)) && defined(PICO_SUPPORT_MCAST))
+
+#ifdef DEBUG_MCAST
+#define multicast_dbg dbg
+#else
+#define multicast_dbg(...) do {} while(0)
+#endif
+
+#define MCAST_EVENT_DELETE_GROUP (0x0)
+#define MCAST_EVENT_CREATE_GROUP (0x1)
+#define MCAST_EVENT_UPDATE_GROUP (0x2)
+#define MCAST_EVENT_QUERY_RECV (0x3)
+#define MCAST_EVENT_REPORT_RECV (0x4)
+#define MCAST_EVENT_TIMER_EXPIRED (0x5)
+
+#define MCAST_MODE_IS_INCLUDE (1)
+#define MCAST_MODE_IS_EXCLUDE (2)
+#define MCAST_CHANGE_TO_INCLUDE_MODE (3)
+#define MCAST_CHANGE_TO_EXCLUDE_MODE (4)
+
+#define MCAST_MODE_IS_INCLUDE (1)
+#define MCAST_MODE_IS_EXCLUDE (2)
+#define MCAST_CHANGE_TO_INCLUDE_MODE (3)
+#define MCAST_CHANGE_TO_EXCLUDE_MODE (4)
+#define MCAST_ALLOW_NEW_SOURCES (5)
+#define MCAST_BLOCK_OLD_SOURCES (6)
+
+typedef int (*mcast_callback)(struct mcast_filter_parameters *);
+
+static void pico_mcast_src_filtering_cleanup(struct mcast_filter_parameters*mcast )
+{
+ struct pico_tree_node *index = NULL, *_tmp = NULL;
+ /* cleanup filters */
+ pico_tree_foreach_safe(index, mcast->allow, _tmp)
+ {
+ pico_tree_delete(mcast->allow, index->keyValue);
+ }
+ pico_tree_foreach_safe(index, mcast->block, _tmp)
+ {
+ pico_tree_delete(mcast->block, index->keyValue);
+ }
+}
+static int pico_mcast_src_filtering_inc_inc(struct mcast_filter_parameters*mcast )
+{
+ struct pico_tree_node *index = NULL;
+ union pico_address *source;
+ /* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
+ if (mcast->p->event == MCAST_EVENT_DELETE_GROUP) {
+ /* TO_IN (B) */
+ mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
+ mcast->filter = mcast->allow;
+ if (mcast->p->MCASTFilter) {
+ pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
+ {
+ if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
+ multicast_dbg("MCAST: Failed to insert entry in tree\n");
+ return -1;
+ }
+ mcast->sources++;
+ }
+ } /* else { allow stays empty } */
+
+ return 0;
+ }
+
+ /* ALLOW (B-A) */
+ /* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
+ if (mcast->p->event == MCAST_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
+ mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
+ else
+ mcast->record_type = MCAST_ALLOW_NEW_SOURCES;
+
+ mcast->filter = mcast->allow;
+ pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
+ {
+ if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
+ multicast_dbg("MCAST: Failed to insert entry in tree\n");
+ return -1;
+ }
+ mcast->sources++;
+ }
+ pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
+ {
+ source = pico_tree_findKey(mcast->allow, index->keyValue);
+ if (source) {
+ pico_tree_delete(mcast->allow, source);
+ mcast->sources--;
+ }
+ }
+ if (!pico_tree_empty(mcast->allow)) /* record type is ALLOW */
+ return 0;
+
+ /* BLOCK (A-B) */
+ mcast->record_type = MCAST_BLOCK_OLD_SOURCES;
+ mcast->filter = mcast->block;
+ pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
+ {
+ if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
+ multicast_dbg("MCAST: Failed to insert entry in tree\n");
+ return -1;
+ }
+ mcast->sources++;
+ }
+ pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
+ {
+ source = pico_tree_findKey(mcast->block, index->keyValue);
+ if (source) {
+ pico_tree_delete(mcast->block, source);
+ mcast->sources--;
+ }
+ }
+ if (!pico_tree_empty(mcast->block)) /* record type is BLOCK */
+ return 0;
+
+ /* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report */
+ (mcast->p)->f = NULL;
+ return MCAST_NO_REPORT;
+}
+
+static int pico_mcast_src_filtering_inc_excl(struct mcast_filter_parameters*mcast )
+{
+ struct pico_tree_node *index = NULL;
+ mcast->record_type = MCAST_CHANGE_TO_EXCLUDE_MODE;
+ mcast->filter = mcast->block;
+ pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
+ {
+ if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
+ multicast_dbg("MCAST: Failed to insert entry in tree\n");
+ return -1;
+ }
+ mcast->sources++;
+ }
+ return 0;
+}
+static int pico_mcast_src_filtering_excl_inc(struct mcast_filter_parameters*mcast )
+{
+ struct pico_tree_node *index = NULL;
+ mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
+ mcast->filter = mcast->allow;
+ if (mcast->p->MCASTFilter) {
+ pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
+ {
+ if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
+ multicast_dbg("MCAST: Failed to insert entry in tree\n");
+ return -1;
+ }
+ mcast->sources++;
+ }
+ } /* else { allow stays empty } */
+
+ return 0;
+}
+static int pico_mcast_src_filtering_excl_excl(struct mcast_filter_parameters*mcast )
+{
+ struct pico_tree_node *index = NULL;
+ struct pico_ip6 *source = NULL;
+ mcast->record_type = MCAST_BLOCK_OLD_SOURCES;
+ mcast->filter = mcast->block;
+ pico_tree_foreach(index, mcast->p->MCASTFilter)
+ {
+ if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
+ multicast_dbg("MCAST: Failed to insert entry in tree\n");
+ return -1;
+ }
+
+ mcast->sources++;
+ }
+ pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
+ {
+ source = pico_tree_findKey(mcast->block, index->keyValue); /* B */
+ if (source) {
+ pico_tree_delete(mcast->block, source);
+ mcast->sources--;
+ }
+ }
+ if (!pico_tree_empty(mcast->block)) /* record type is BLOCK */
+ return 0;
+
+ /* ALLOW (A-B) */
+ mcast->record_type = MCAST_ALLOW_NEW_SOURCES;
+ mcast->filter = mcast->allow;
+ pico_tree_foreach(index, &mcast->g->MCASTSources)
+ {
+ if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
+ multicast_dbg("MCAST: Failed to insert entry in tree\n");
+ return -1;
+ }
+ mcast->sources++;
+ }
+ pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
+ {
+ source = pico_tree_findKey(mcast->allow, index->keyValue); /* A */
+ if (source) {
+ pico_tree_delete(mcast->allow, source);
+ mcast->sources--;
+ }
+ }
+ if (!pico_tree_empty(mcast->allow)) /* record type is ALLOW */
+ return 0;
+
+ /* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report */
+ mcast->p->f = NULL;
+ return MCAST_NO_REPORT;
+}
+static const mcast_callback mcast_filter_state[2][2] =
+{
+ { pico_mcast_src_filtering_excl_excl, pico_mcast_src_filtering_excl_inc},
+ { pico_mcast_src_filtering_inc_excl, pico_mcast_src_filtering_inc_inc }
+};
+int8_t pico_mcast_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
+{
+ int ret = -1;
+ /* "non-existent" state of filter mode INCLUDE and empty source list */
+ if (p->event == MCAST_EVENT_DELETE_GROUP) {
+ p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
+ p->MCASTFilter = NULL;
+ }
+
+ if (p->event == MCAST_EVENT_QUERY_RECV)
+ return 0;
+
+ pico_mcast_src_filtering_cleanup(filter);
+
+ if(filter->g->filter_mode <= PICO_IP_MULTICAST_INCLUDE )
+ {
+ if(p->filter_mode <= PICO_IP_MULTICAST_INCLUDE)
+ {
+ ret = mcast_filter_state[filter->g->filter_mode][p->filter_mode](filter);
+ }
+ }
+
+ return (int8_t) ret;
+}
+#endif
diff --git a/ext/picotcp/modules/pico_mcast.h b/ext/picotcp/modules/pico_mcast.h
new file mode 100644
index 0000000..dc9146d
--- /dev/null
+++ b/ext/picotcp/modules/pico_mcast.h
@@ -0,0 +1,53 @@
+#ifndef INCLUDE_PICO_MCAST
+#define INCLUDE_PICO_MCAST
+
+#define MCAST_MODE_IS_INCLUDE (1)
+#define MCAST_MODE_IS_EXCLUDE (2)
+#define MCAST_CHANGE_TO_INCLUDE_MODE (3)
+#define MCAST_CHANGE_TO_EXCLUDE_MODE (4)
+#define MCAST_ALLOW_NEW_SOURCES (5)
+#define MCAST_BLOCK_OLD_SOURCES (6)
+#define MCAST_EVENT_DELETE_GROUP (0x0)
+#define MCAST_EVENT_CREATE_GROUP (0x1)
+#define MCAST_EVENT_UPDATE_GROUP (0x2)
+#define MCAST_EVENT_QUERY_RECV (0x3)
+#define MCAST_EVENT_REPORT_RECV (0x4)
+#define MCAST_EVENT_TIMER_EXPIRED (0x5)
+#define MCAST_NO_REPORT (1)
+
+PACKED_STRUCT_DEF mcast_parameters {
+ uint8_t event;
+ uint8_t state;
+ uint8_t general_query;
+ uint8_t filter_mode;
+ uint8_t last_host;
+ uint16_t max_resp_time;
+ union pico_address mcast_link;
+ union pico_address mcast_group;
+ struct pico_tree *MCASTFilter;
+ struct pico_frame *f;
+};
+
+PACKED_STRUCT_DEF pico_mcast_group {
+ uint8_t filter_mode;
+ uint16_t reference_count;
+ union pico_address mcast_addr;
+ struct pico_tree MCASTSources;
+};
+
+PACKED_STRUCT_DEF mcast_filter_parameters {
+ struct mcast_parameters *p;
+ struct pico_tree *allow;
+ struct pico_tree *block;
+ struct pico_tree *filter;
+ uint16_t sources;
+ uint8_t proto;
+ uint8_t record_type;
+ struct pico_mcast_group *g;
+ union pico_link *link;
+};
+
+
+extern int8_t pico_mcast_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p);
+
+#endif
diff --git a/ext/picotcp/modules/pico_mdns.c b/ext/picotcp/modules/pico_mdns.c
index 6b5c5e9..2d6cf07 100644
--- a/ext/picotcp/modules/pico_mdns.c
+++ b/ext/picotcp/modules/pico_mdns.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2014-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Author: Toon Stegen, Jelle De Vleeschouwer
*********************************************************************/
@@ -16,8 +16,11 @@
#ifdef PICO_SUPPORT_MDNS
/* --- Debugging --- */
+#ifdef DEBUG_MDNS
+#define mdns_dbg dbg
+#else
#define mdns_dbg(...) do {} while(0)
-//#define mdns_dbg dbg
+#endif
#define PICO_MDNS_QUERY_TIMEOUT (10000) /* Ten seconds */
#define PICO_MDNS_RR_TTL_TICK (1000) /* One second */
@@ -41,6 +44,9 @@
#define PICO_MDNS_SECTION_AUTHORITIES (1)
#define PICO_MDNS_SETCTIO_ADDITIONALS (2)
+#define PICO_MDNS_CTREE_DESTROY(rtree) \
+ pico_tree_destroy((rtree), pico_mdns_cookie_delete);
+
/* --- Question flags --- */
#define PICO_MDNS_QUESTION_FLAG_PROBE (0x01u)
#define PICO_MDNS_QUESTION_FLAG_NO_PROBE (0x00u)
@@ -87,7 +93,7 @@
/* ****************************************************************************
* mDNS cookie
- * ****************************************************************************/
+ * *****************************************************************************/
struct pico_mdns_cookie
{
pico_dns_qtree qtree; /* Question tree */
@@ -115,8 +121,10 @@ static struct pico_ip4 inaddr_any = {
/* ****************************************************************************
* Hostname for this machine, only 1 hostname can be set.
- * ****************************************************************************/
+ * Following RFC6267: 15.4 Recommendation
+ * *****************************************************************************/
static char *_hostname = NULL;
+
static void (*init_callback)(pico_mdns_rtree *, char *, void *) = 0;
/* ****************************************************************************
@@ -156,6 +164,7 @@ pico_mdns_record_cmp( void *a, void *b )
if (!a || !b) {
if (!a && !b)
return 0;
+
pico_err = PICO_ERR_EINVAL;
return -1; /* Don't want a wrong result when NULL-pointers are passed */
}
@@ -210,16 +219,33 @@ pico_mdns_cookie_cmp( void *ka, void *kb )
return 0;
}
+/*
+ * Hash to identify mDNS timers with
+ */
+static uint32_t mdns_hash = 0;
+
+/*
+ * mDNS specific timer creation, to identify if timers are
+ * created by mDNS module
+ */
+static uint32_t
+pico_mdns_timer_add(pico_time expire,
+ void (*timer)(pico_time, void *),
+ void *arg)
+{
+ return pico_timer_add_hashed(expire, timer, arg, mdns_hash);
+}
+
#if PICO_MDNS_ALLOW_CACHING == 1
/* Cache records from mDNS peers on the network */
-PICO_TREE_DECLARE(Cache, &pico_mdns_record_cmp);
+static PICO_TREE_DECLARE(Cache, &pico_mdns_record_cmp);
#endif
/* My records for which I want to have the authority */
-PICO_TREE_DECLARE(MyRecords, &pico_mdns_record_cmp_name_type);
+static PICO_TREE_DECLARE(MyRecords, &pico_mdns_record_cmp_name_type);
/* Cookie-tree */
-PICO_TREE_DECLARE(Cookies, &pico_mdns_cookie_cmp);
+static PICO_TREE_DECLARE(Cookies, &pico_mdns_cookie_cmp);
/* ****************************************************************************
* MARK: PROTOTYPES */
@@ -455,30 +481,30 @@ static struct pico_mdns_record *
pico_mdns_record_copy( struct pico_mdns_record *record )
{
struct pico_mdns_record *copy = NULL;
-
+
/* Check params */
if (!record) {
pico_err = PICO_ERR_EINVAL;
return NULL;
}
-
+
/* Provide space for the copy */
if (!(copy = PICO_ZALLOC(sizeof(struct pico_mdns_record)))) {
pico_err = PICO_ERR_ENOMEM;
return NULL;
}
-
+
/* Copy the DNS record */
if (!(copy->record = pico_dns_record_copy(record->record))) {
PICO_FREE(copy);
return NULL;
}
-
+
/* Copy the fields */
copy->current_ttl = record->current_ttl;
copy->flags = record->flags;
copy->claim_id = record->claim_id;
-
+
return copy;
}
@@ -510,8 +536,13 @@ pico_mdns_rtree_find_name( pico_mdns_rtree *tree,
if (record && strcasecmp(record->record->rname, name) == 0) {
if (copy)
record = pico_mdns_record_copy(record);
+
if (record)
- pico_tree_insert(&hits, record);
+ if (pico_tree_insert(&hits, record) != NULL)
+ /* either key was already in there, or couldn't be inserted. */
+ /* Only delete record if it was copied */
+ if (copy)
+ pico_mdns_record_delete((void **)&record);
}
}
@@ -535,9 +566,15 @@ pico_mdns_rtree_find_name_type( pico_mdns_rtree *tree,
{
PICO_MDNS_RTREE_DECLARE(hits);
- struct pico_dns_record_suffix test_dns_suffix = { 0, 1, 0, 0 };
- struct pico_dns_record test_dns_record = { 0 };
- struct pico_mdns_record test = { 0 };
+ struct pico_dns_record_suffix test_dns_suffix = {
+ 0, 1, 0, 0
+ };
+ struct pico_dns_record test_dns_record = {
+ 0
+ };
+ struct pico_mdns_record test = {
+ 0
+ };
struct pico_tree_node *node = NULL;
struct pico_mdns_record *record = NULL;
test_dns_record.rsuffix = &test_dns_suffix;
@@ -558,8 +595,15 @@ pico_mdns_rtree_find_name_type( pico_mdns_rtree *tree,
if ((record) && (0 == pico_mdns_record_cmp_name_type(record, &test))) {
if (copy)
record = pico_mdns_record_copy(record);
- if (record)
- pico_tree_insert(&hits, record);
+
+ if (record){
+ if (pico_tree_insert(&hits, record) != NULL) {
+ /* either key was already in there, or couldn't be inserted. */
+ /* Only delete record if it was copied */
+ if (copy)
+ pico_mdns_record_delete((void **)&record);
+ }
+ }
}
}
@@ -615,9 +659,15 @@ pico_mdns_rtree_del_name_type( pico_mdns_rtree *tree,
{
struct pico_tree_node *node = NULL, *next = NULL;
struct pico_mdns_record *record = NULL;
- struct pico_dns_record_suffix test_dns_suffix = { 0, 1, 0, 0 };
- struct pico_dns_record test_dns_record = { 0 };
- struct pico_mdns_record test = { 0 };
+ struct pico_dns_record_suffix test_dns_suffix = {
+ 0, 1, 0, 0
+ };
+ struct pico_dns_record test_dns_record = {
+ 0
+ };
+ struct pico_mdns_record test = {
+ 0
+ };
test_dns_record.rsuffix = &test_dns_suffix;
test.record = &test_dns_record;
@@ -660,7 +710,7 @@ pico_mdns_record_copy_with_new_name( struct pico_mdns_record *record,
uint16_t slen = (uint16_t)(pico_dns_strlen(new_rname) + 1u);
/* Check params */
- if (pico_dns_check_namelen(slen)) {
+ if (!new_rname || pico_dns_check_namelen(slen)) {
pico_err = PICO_ERR_EINVAL;
return NULL;
}
@@ -722,7 +772,11 @@ pico_mdns_generate_new_records( pico_mdns_rtree *conflict_records,
PICO_MDNS_RECORD_CURRENTLY_PROBING));
/* Add the record to the new tree */
- pico_tree_insert(&new_records, new_record);
+ if (pico_tree_insert(&new_records, new_record)) {
+ mdns_dbg("Could not add new non-conflicting record to the tree!\n");
+ pico_mdns_record_delete((void **)&new_record);
+ return new_records;
+ }
/* Delete the old conflicting record */
record = pico_tree_delete(conflict_records, record);
@@ -767,8 +821,13 @@ pico_mdns_record_resolve_conflict( struct pico_mdns_record *record,
copy = pico_mdns_record_copy_with_new_name(record, new_name);
PICO_FREE(new_name);
- if (copy)
- pico_tree_insert(&new_records, copy);
+ if (copy){
+ if (pico_tree_insert(&new_records, copy)) {
+ mdns_dbg("MDNS: Failed to insert copy in tree\n");
+ pico_mdns_record_delete((void **)©);
+ return -1;
+ }
+ }
/* Step 3: delete conflicting record from my records */
pico_tree_delete(&MyRecords, record);
@@ -804,42 +863,42 @@ pico_mdns_record_am_i_lexi_later( struct pico_mdns_record *my_record,
return -1;
}
- /*
- * First compare the record class (excluding cache-flush bit described in
+ /*
+ * First compare the record class (excluding cache-flush bit described in
* section 10.2)
* The numerically greater class wins
*/
mclass = PICO_MDNS_CLR_MSB_BE(my->rsuffix->rclass);
pclass = PICO_MDNS_CLR_MSB_BE(peer->rsuffix->rclass);
- if ((dif = (int)((int)mclass - (int)pclass))){
+ if ((dif = (int)((int)mclass - (int)pclass))) {
return dif;
}
/* Second, compare the rrtypes */
mtype = (my->rsuffix->rtype);
ptype = (peer->rsuffix->rtype);
- if ((dif = (int)((int)mtype - (int)ptype))){
+ if ((dif = (int)((int)mtype - (int)ptype))) {
return dif;
}
/* Third compare binary content of rdata (no regard for meaning or structure) */
- /* When using name compression, names MUST be uncompressed before comparison. See secion 8.2 in RFC 6762
- This is already the case, but we won't check for it here.
- The current execution stack to get here is:
- > pico_mdns_handle_data_as_answers_generic
- > > pico_dns_record_decompress
- > > pico_mdns_handle_single_authority
- > > > pico_mdns_cookie_apply_spt
- > > > > pico_mdns_record_am_i_lexi_later
+ /* When using name compression, names MUST be uncompressed before comparison. See secion 8.2 in RFC6762
+ This is already the case, but we won't check for it here.
+ The current execution stack to get here is:
+ > pico_mdns_handle_data_as_answers_generic
+ > > pico_dns_record_decompress
+ > > pico_mdns_handle_single_authority
+ > > > pico_mdns_cookie_apply_spt
+ > > > > pico_mdns_record_am_i_lexi_later
- Make sure pico_dns_record_decompress is executed before pico_mdns_record_am_i_lexi_later gets called, if problems ever arise with this function.
- */
+ Make sure pico_dns_record_decompress is executed before pico_mdns_record_am_i_lexi_later gets called, if problems ever arise with this function.
+ */
/* Then compare rdata */
return pico_dns_rdata_cmp(my->rdata, peer->rdata,
short_be(my->rsuffix->rdlength),
- short_be(peer->rsuffix->rdlength), 0);
+ short_be(peer->rsuffix->rdlength), PICO_DNS_CASE_SENSITIVE);
}
/* ****************************************************************************
@@ -861,8 +920,9 @@ pico_mdns_record_delete( void **record )
}
/* Delete DNS record contained */
- if (((*rr)->record))
+ if (((*rr)->record)) {
pico_dns_record_delete((void **)&((*rr)->record));
+ }
/* Delete the record itself */
PICO_FREE(*rr);
@@ -871,29 +931,6 @@ pico_mdns_record_delete( void **record )
return 0;
}
-/* ****************************************************************************
- * Set the MSB of the QCLASS field of a DNS record depending on flags.
- *
- * @param record DNS record you want the set or clear the class MSB for.
- * @param flags Depending on this the MSB will be set or not. Can be either
- * PICO_MDNS_RECORD_UNIQUE or PICO_MDNS_RECORD_SHARED
- * @return 0 on success, something else on failure.
- * ****************************************************************************/
-static inline int
-pico_mdns_record_set_class( struct pico_dns_record *record,
- uint8_t flags )
-{
- uint16_t c = PICO_DNS_CLASS_IN;
- PICO_MDNS_SET_MSB(c);
- c = short_be(c);
-
- /* Set the MSB of the rclass field according to the mDNS format */
- if (!((flags) & PICO_MDNS_RECORD_SHARED))
- record->rsuffix->rclass = c;
-
- return 0;
-}
-
/* ****************************************************************************
* Creates a single standalone mDNS resource record with given name, type and
* data.
@@ -924,31 +961,41 @@ pico_mdns_record_create( const char *url,
{
struct pico_mdns_record *record = NULL;
uint16_t len = 0;
+ uint16_t cl = 0;
/* Check params */
if (!url || !_rdata) {
pico_err = PICO_ERR_EINVAL;
return NULL;
- }
+ } /* Block 1, 2 paths */
/* Provide space for the new mDNS resource record */
if (!(record = PICO_ZALLOC(sizeof(struct pico_mdns_record)))) {
pico_err = PICO_ERR_ENOMEM;
return NULL;
- }
-
- /* Try to create the actual DNS record */
- if (!(record->record = pico_dns_record_create(url, _rdata, datalen,
- &len, rtype,
- PICO_DNS_CLASS_IN, rttl))) {
- mdns_dbg("Could not create DNS record for mDNS!\n");
- PICO_FREE(record);
- return NULL;
- }
+ } /* Block 2, 1 path */
+ else {
+ /* Try to create the actual DNS record */
+ if (!(record->record = pico_dns_record_create(url, _rdata, datalen,
+ &len, rtype,
+ PICO_DNS_CLASS_IN, rttl))) {
+ mdns_dbg("Could not create DNS record for mDNS!\n");
+ PICO_FREE(record);
+ return NULL;
+ } /* Block 3, 2 paths */
+ } /* Block 4, Block 3 = 2 paths */
+ /* Block 5, (Block 4 + Block 2) * Block 1 = 6 paths */
/* Initialise fields */
record->current_ttl = rttl;
- pico_mdns_record_set_class(record->record, flags);
+
+ /* Set the MSB of the DNS class if it's a unique record */
+ if (!((flags) & PICO_MDNS_RECORD_SHARED)) {
+ cl = record->record->rsuffix->rclass;
+ record->record->rsuffix->rclass = PICO_MDNS_SET_MSB_BE(cl);
+ } /* Block 6, 2 paths */
+ /* Block 7, Block 6 * Block 5 * Block 1 = 12 paths */
+
record->flags = flags;
record->claim_id = 0;
@@ -966,8 +1013,10 @@ pico_mdns_record_create( const char *url,
* @return Returns 0 on success, something else on failure.
* ****************************************************************************/
static int
-pico_mdns_cookie_delete( struct pico_mdns_cookie **c )
+pico_mdns_cookie_delete( void **ptr )
{
+ struct pico_mdns_cookie **c = (struct pico_mdns_cookie **)ptr;
+
/* Check params */
if (!c || !(*c)) {
pico_err = PICO_ERR_EINVAL;
@@ -1032,10 +1081,11 @@ pico_mdns_cookie_create( pico_dns_qtree qtree,
/* ****************************************************************************
* Apply Simultaneous Probe Tiebreakin (S.P.T.) on a probe-cookie.
+ * See RFC6762: 8.2. Simultaneous Probe Tiebreaking
*
* @param cookie Cookie which contains the record which is simult. probed.
* @param answer Authority record received from peer which is simult. probed.
- * @return 0 when SPT is applied correctly, 0 otherwise.
+ * @return 0 when SPT is applied correctly, -1 otherwise.
* ****************************************************************************/
static int
pico_mdns_cookie_apply_spt( struct pico_mdns_cookie *cookie,
@@ -1067,9 +1117,33 @@ pico_mdns_cookie_apply_spt( struct pico_mdns_cookie *cookie,
pico_timer_cancel(cookie->send_timer);
cookie->timeout = PICO_MDNS_COOKIE_TIMEOUT;
cookie->count = PICO_MDNS_PROBE_COUNT;
- cookie->send_timer = pico_timer_add(1000, pico_mdns_send_probe_packet,
+ cookie->send_timer = pico_mdns_timer_add(1000, pico_mdns_send_probe_packet,
cookie);
- mdns_dbg("Probing postponed with 1s because of S.P.T.\n");
+ if (!cookie->send_timer) {
+ mdns_dbg("cookie_apply_spt: failed to start timer\n");
+ return -1;
+ }
+ mdns_dbg("Probing postponed by one second because of S.P.T.\n");
+ }
+
+ return 0;
+}
+
+static int
+pico_mdns_cookie_del_questions( struct pico_mdns_cookie *cookie,
+ char *rname )
+{
+ uint16_t qc = 0;
+
+ /* Step 1: Remove question with that name from cookie */
+ pico_dns_qtree_del_name(&(cookie->qtree), rname);
+ cookie->antree.root = &LEAF;
+
+ /* Check if there are no questions left, cancel events if so and delete */
+ if (!(qc = pico_tree_count(&(cookie->qtree)))) {
+ pico_timer_cancel(cookie->send_timer);
+ cookie = pico_tree_delete(&Cookies, cookie);
+ pico_mdns_cookie_delete((void **)&cookie);
}
return 0;
@@ -1096,7 +1170,6 @@ pico_mdns_cookie_resolve_conflict( struct pico_mdns_cookie *cookie,
char *new_name = NULL;
void (*callback)(pico_mdns_rtree *, char *, void *);
void *arg = NULL;
- uint16_t qc = 0;
int retval;
/* Check params */
@@ -1108,33 +1181,35 @@ pico_mdns_cookie_resolve_conflict( struct pico_mdns_cookie *cookie,
/* Convert rname to url */
mdns_dbg("CONFLICT for probe query with name '%s' occurred!\n", rname);
- /* DNS conflict is case-insensitive. However, we want to keep the original
- * capitalisation for the new probe. */
- pico_tree_foreach(node, &(cookie->qtree)) {
- question = (struct pico_dns_question *)node->keyValue;
- if ((question) && (strcasecmp(question->qname, rname) == 0))
- /* Create a new name depending on current name */
- new_name = pico_mdns_resolve_name_conflict(question->qname);
- }
-
- /* Step 1: Remove question with that name from cookie and store some
- * useful information */
- pico_dns_qtree_del_name(&(cookie->qtree), rname);
+ /* Store some information about a cookie for later on */
antree = cookie->antree;
callback = cookie->callback;
arg = cookie->arg;
- cookie->antree.root = &LEAF;
- /* Check if there are no questions left, cancel events if so and delete */
- if (!(qc = pico_tree_count(&(cookie->qtree)))) {
- pico_timer_cancel(cookie->send_timer);
- cookie = pico_tree_delete(&Cookies, cookie);
- pico_mdns_cookie_delete(&cookie);
+ /* Find the first question in the cookie with the name for which
+ * the conflict occured. When found, generate a new name.
+ *
+ * DNS conflict is case-insensitive. However, we want to keep the original
+ * capitalisation for the new probe. */
+ pico_tree_foreach(node, &(cookie->qtree)) {
+ question = (struct pico_dns_question *)node->keyValue;
+ if ((question) && (strcasecmp(question->qname, rname) == 0)) {
+ /* Create a new name depending on current name */
+ new_name = pico_mdns_resolve_name_conflict(question->qname);
+
+ /* Step 1: Check if the new name succeeded, if not: error. */
+ if (!new_name) {
+ /* Delete questions from cookie even if generating a new name failed */
+ pico_mdns_cookie_del_questions(cookie, rname);
+ return -1;
+ }
+
+ break;
+ }
}
- /* Step 2: Check if the new name succeeded, if not: error. */
- if (!(new_name))
- return -1;
+ /* Step 2: Remove questions with this name from the cookie */
+ pico_mdns_cookie_del_questions(cookie, rname);
/* Step 3: Create records with new name for the records with that name */
new_records = pico_mdns_generate_new_records(&antree, rname, new_name);
@@ -1196,29 +1271,37 @@ pico_mdns_my_records_add( pico_mdns_rtree *records, uint8_t reclaim )
struct pico_mdns_record *record = NULL;
static uint8_t claim_id_count = 0;
- if (!reclaim)
+ if (!reclaim) {
++claim_id_count;
+ }
/* Iterate over record vector */
pico_tree_foreach(node, records) {
record = node->keyValue;
if (record) {
/* Set probed flag if record is a shared record */
- if (IS_SHARED_RECORD(record))
+ if (IS_SHARED_RECORD(record)) {
PICO_MDNS_SET_FLAG(record->flags, PICO_MDNS_RECORD_PROBED);
+ }
/* If record is not claimed again, set new claim-ID */
- if (!reclaim)
+ if (!reclaim) {
record->claim_id = claim_id_count;
- pico_tree_insert(&MyRecords, record);
+ }
+
+ if (pico_tree_insert(&MyRecords, record) == &LEAF) {
+ mdns_dbg("MDNS: Failed to insert record in tree\n");
+ return -1;
+ }
}
}
return 0;
}
/* ****************************************************************************
- * Generates a tree of all My Records for which the probe flag already has
- * been set. Copies the records from MyRecords into a new tree.
+ * Generates a tree of all My Records for which the probe flag has already
+ * been set, and for which the CLAIMED flag has NOT been set.
+ * Copies the records from MyRecords into a new tree.
*
* @return Tree with all records in MyRecords with the PROBED-flag set.
* ****************************************************************************/
@@ -1232,10 +1315,13 @@ pico_mdns_my_records_find_probed( void )
/* Iterate over MyRecords */
pico_tree_foreach(node, &MyRecords) {
record = node->keyValue;
+
+ /* IS_RECORD_VERIFIED() checks the PICO_MDNS_RECORD_PROBED flag */
if (record && IS_RECORD_VERIFIED(record) && !IS_RECORD_CLAIMED(record)) {
copy = pico_mdns_record_copy(record);
- if (copy && pico_tree_insert(&probed, copy))
+ if (copy && pico_tree_insert(&probed, copy)) {
pico_mdns_record_delete((void **)©);
+ }
}
}
@@ -1243,7 +1329,7 @@ pico_mdns_my_records_find_probed( void )
}
/* ****************************************************************************
- * Generates a tree of all My Records for which the probe flag has not yet
+ * Generates a tree of all My Records for which the PROBED-flag has not yet
* been set. Copies the record from MyRecords into a new tree.
*
* @return Tree with all records in MyRecords with the PROBED-flag not set.
@@ -1251,7 +1337,7 @@ pico_mdns_my_records_find_probed( void )
static pico_mdns_rtree
pico_mdns_my_records_find_to_probe( void )
{
- PICO_MDNS_RTREE_DECLARE(toprobe);
+ PICO_MDNS_RTREE_DECLARE(to_probe);
struct pico_tree_node *node = NULL;
struct pico_mdns_record *record = NULL, *copy = NULL;
@@ -1265,11 +1351,11 @@ pico_mdns_my_records_find_to_probe( void )
/* Set record to currently being probed status */
record->flags |= PICO_MDNS_RECORD_CURRENTLY_PROBING;
copy = pico_mdns_record_copy(record);
- if (copy && pico_tree_insert(&toprobe, copy))
+ if (copy && pico_tree_insert(&to_probe, copy))
pico_mdns_record_delete((void **)©);
}
}
- return toprobe;
+ return to_probe;
}
/* ****************************************************************************
@@ -1291,7 +1377,10 @@ pico_mdns_my_records_claimed_id( uint8_t claim_id,
record = node->keyValue;
if (record && record->claim_id == claim_id) {
if (IS_RECORD_VERIFIED(record)) {
- pico_tree_insert(reg_records, record);
+ if (pico_tree_insert(reg_records, record) == &LEAF) {
+ mdns_dbg("MDNS: Failed to insert record in tree\n");
+ return 0;
+ }
} else {
return 0;
}
@@ -1303,8 +1392,9 @@ pico_mdns_my_records_claimed_id( uint8_t claim_id,
/* ****************************************************************************
* Marks mDNS resource records in the tree as registered. Checks MyRecords for
- * for other records with the same claim ID. If all records with the same claim
- * ID as the records in the tree are claimed, the callback will get called.
+ * for other records with the same claim ID. If all records with the same
+ * claim ID as the records in the tree are claimed,
+ * the callback will get called.
*
* @param rtree Tree with mDNS records that are registered.
* @param callback Callback will get called when all records are registered.
@@ -1325,17 +1415,21 @@ pico_mdns_my_records_claimed( pico_mdns_rtree rtree,
/* Iterate over records and set the PROBED flag */
pico_tree_foreach(node, &rtree) {
if ((record = node->keyValue)) {
- if (!claim_id)
+ if (!claim_id) {
claim_id = record->claim_id;
+ }
}
- if ((myrecord = pico_tree_findKey(&MyRecords, record)))
+ if ((myrecord = pico_tree_findKey(&MyRecords, record))) {
PICO_MDNS_SET_FLAG(myrecord->flags, PICO_MDNS_RECORD_CLAIMED);
+ }
}
/* If all_claimed is still true */
- if (pico_mdns_my_records_claimed_id(claim_id, &claimed_records))
+ if (pico_mdns_my_records_claimed_id(claim_id, &claimed_records)) {
callback(&claimed_records, _hostname, arg);
+ }
+
pico_tree_destroy(&claimed_records, NULL);
mdns_dbg(">>>>>> DONE - CLAIM SESSION: %d\n", claim_id);
@@ -1345,7 +1439,8 @@ pico_mdns_my_records_claimed( pico_mdns_rtree rtree,
/* ****************************************************************************
* Makes sure the cache flush bit is set of the records which are probed, and
- * set the corresponding MyRecords to 'being probed'-state.
+ * set the corresponding MyRecords from 'being probed' to
+ * 'has been probed'-state.
*
* @param records mDNS records which are probed.
* ****************************************************************************/
@@ -1361,16 +1456,18 @@ pico_mdns_my_records_probed( pico_mdns_rtree *records )
PICO_MDNS_SET_MSB_BE(record->record->rsuffix->rclass);
if ((found = pico_tree_findKey(&MyRecords, record))) {
if (IS_HOSTNAME_RECORD(found)) {
- if (_hostname)
+ if (_hostname) {
PICO_FREE(_hostname);
+ }
_hostname = pico_dns_qname_to_url(found->record->rname);
}
- PICO_MDNS_SET_FLAG(found->flags, PICO_MDNS_RECORD_PROBED);
PICO_MDNS_CLR_FLAG(found->flags, PICO_MDNS_RECORD_CURRENTLY_PROBING);
- } else
+ PICO_MDNS_SET_FLAG(found->flags, PICO_MDNS_RECORD_PROBED);
+ } else{
mdns_dbg("Could not find my corresponding record...\n");
+ }
}
}
}
@@ -1400,6 +1497,21 @@ pico_mdns_cache_update_ttl( struct pico_mdns_record *record,
}
}
+static int
+pico_mdns_cache_flush_name( char *name, struct pico_dns_record_suffix *suffix )
+{
+ /* Check if cache flush bit is set */
+ if (PICO_MDNS_IS_MSB_SET(short_be(suffix->rclass))) {
+ mdns_dbg("FLUSH - Cache flush bit was set, triggered flush.\n");
+ if (pico_mdns_rtree_del_name_type(&Cache, name, short_be(suffix->rtype))) {
+ mdns_dbg("Could not flush records from cache!\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
/* ****************************************************************************
* Adds a mDNS record to the cache.
*
@@ -1412,7 +1524,6 @@ pico_mdns_cache_add( struct pico_mdns_record *record )
{
struct pico_dns_record_suffix *suffix = NULL;
char *name = NULL;
- uint16_t type = 0;
uint32_t rttl = 0;
/* Check params */
@@ -1420,33 +1531,32 @@ pico_mdns_cache_add( struct pico_mdns_record *record )
pico_err = PICO_ERR_EINVAL;
return -1;
}
+ /* 2 paths */
suffix = record->record->rsuffix;
name = record->record->rname;
- type = short_be(suffix->rtype);
rttl = long_be(suffix->rttl);
- /* Check if cache flush bit is set */
- if (PICO_MDNS_IS_MSB_SET(short_be(suffix->rclass))) {
- mdns_dbg("FLUSH - Cache flush bit was set, triggered flush.\n");
- if (pico_mdns_rtree_del_name_type(&Cache, name, type)) {
- mdns_dbg("Could not flush records from cache!\n");
- return -1;
- }
+ if (pico_mdns_cache_flush_name(name, suffix)) {
+ return -1;
}
+ /* 4 paths */
/* Check if the TTL is not 0*/
- if (!rttl)
+ if (!rttl) {
return -1;
- /* Set current TTL to the original TTL before inserting */
- record->current_ttl = rttl;
+ } else {
+ /* Set current TTL to the original TTL before inserting */
+ record->current_ttl = rttl;
- if (pico_tree_insert(&Cache, record) != NULL)
- return -1;
+ if (pico_tree_insert(&Cache, record) != NULL)
+ return -1;
- mdns_dbg("RR cached. TICK TACK TICK TACK...\n");
+ mdns_dbg("RR cached. TICK TACK TICK TACK...\n");
- return 0;
+ return 0;
+ }
+ /* 12 paths */
}
/* ****************************************************************************
@@ -1575,7 +1685,7 @@ pico_mdns_cookies_check_timeouts( void )
/* Delete cookie */
cookie = pico_tree_delete(&Cookies, cookie);
- pico_mdns_cookie_delete(&cookie);
+ pico_mdns_cookie_delete((void **)&cookie);
/* If the request was for a reconfirmation of a record,
flush the corresponding record after the timeout */
@@ -1605,7 +1715,10 @@ pico_mdns_tick( pico_time now, void *_arg )
pico_mdns_cookies_check_timeouts();
/* Schedule new tick */
- pico_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL);
+ if (!pico_mdns_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL)) {
+ mdns_dbg("MDNS: Failed to start tick timer\n");
+ /* TODO Not ticking anymore, what to do? */
+ }
}
/* MARK: v MDNS PACKET UTILITIES */
@@ -1616,11 +1729,12 @@ pico_mdns_tick( pico_time now, void *_arg )
* @param packet Packet buffer in memory
* @param len Size of the packet in bytes
* @return 0 When the packet is passed successfully on to the lower layers of
- * picoTCP. Doesn't mean the packet is successfully send on the wire.
+ * picoTCP. Doesn't mean the packet is successfully sent on the wire.
* ****************************************************************************/
static int
pico_mdns_send_packet( pico_dns_packet *packet, uint16_t len )
{
+ /* TODO: why only ipv4 support? */
struct pico_ip4 dst4;
/* Set the destination address to the mDNS multicast-address */
@@ -1683,6 +1797,14 @@ pico_mdns_unicast_reply( pico_dns_rtree *unicast_tree,
if (!local_addr) {
mdns_dbg("Peer not on same link!\n");
/* Forced response via multicast */
+
+ /* RFC6762: 18.6: In both multicast query and response messages,
+ the RD bit SHOULD be zero on transmission. In
+ pico_dns_fill_packet_header, the RD bit is set to
+ PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
+ packet->rd = PICO_DNS_RD_NO_DESIRE;
+
+
if (pico_mdns_send_packet(packet, len) != (int)len) {
mdns_dbg("Could not send multicast response!\n");
return -1;
@@ -1696,6 +1818,7 @@ pico_mdns_unicast_reply( pico_dns_rtree *unicast_tree,
mdns_dbg("Unicast response sent successfully!\n");
}
+
PICO_FREE(packet);
}
@@ -1726,6 +1849,12 @@ pico_mdns_multicast_reply( pico_dns_rtree *multicast_tree,
packet->id = 0;
+ /* RFC6762: 18.6: In both multicast query and response messages,
+ the RD bit SHOULD be zero on transmission.
+ In pico_dns_fill_packet_header, the RD bit is set to
+ PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
+ packet->rd = PICO_DNS_RD_NO_DESIRE;
+
/* Send the packet via multicast */
if (pico_mdns_send_packet(packet, len) != (int)len) {
mdns_dbg("Could not send multicast response!\n");
@@ -1764,7 +1893,10 @@ pico_tree_merge( struct pico_tree *dest, struct pico_tree *src )
/* Insert source nodes */
pico_tree_foreach(node, src) {
if (node->keyValue) {
- pico_tree_insert(dest, node->keyValue);
+ if (pico_tree_insert(dest, node->keyValue) == &LEAF) {
+ mdns_dbg("MDNS: Failed to insert record in tree\n");
+ return -1;
+ }
}
}
@@ -1887,7 +2019,10 @@ pico_mdns_handle_cookie_with_answer( struct pico_mdns_cookie *cookie,
if (cookie->callback) {
/* RCVD Answer on query, callback with answer. Callback is
* responsible for aggregating all the received answers. */
- pico_tree_insert(&antree, answer);
+ if (pico_tree_insert(&antree, answer) == &LEAF) {
+ mdns_dbg("MDNS: Failed to insert answer in tree\n");
+ return -1;
+ }
cookie->callback(&antree, NULL, cookie->arg);
}
} else { /* Don't handle answer cookies with answer */
@@ -2041,6 +2176,10 @@ pico_mdns_handle_data_as_answers_generic( uint8_t **ptr,
return -1;
}
+ /* TODO: When receiving multiple authoritative answers, */
+ /* they should be sorted in lexicographical order */
+ /* (just like in pico_mdns_record_am_i_lexi_later) */
+
for (i = 0; i < count; i++) {
/* Set rname of the record to the correct location */
answer.rname = (char *)(*ptr);
@@ -2112,11 +2251,19 @@ pico_mdns_sort_unicast_multicast( pico_mdns_rtree *answers,
record = node->keyValue;
if ((record = node->keyValue)) {
if (IS_UNICAST_REQUESTED(record)) {
- if (record->record)
- pico_tree_insert(unicast_tree, record->record);
+ if (record->record){
+ if (pico_tree_insert(unicast_tree, record->record) == &LEAF) {
+ mdns_dbg("MDNS: Failed to instert unicast record in tree\n");
+ return -1;
+ }
+ }
} else {
- if (record->record)
- pico_tree_insert(multicast_tree, record->record);
+ if (record->record){
+ if (pico_tree_insert(multicast_tree, record->record) == &LEAF) {
+ mdns_dbg("MDNS: Failed to instert multicast record in tree\n");
+ return -1;
+ }
+ }
}
}
}
@@ -2247,16 +2394,20 @@ pico_mdns_additionals_add_nsec( pico_mdns_rtree *artree,
pico_tree_foreach(node, artree) {
if (node != &LEAF && (record = node->keyValue)) {
type = short_be(record->record->rsuffix->rtype);
- if (PICO_DNS_TYPE_NSEC == type) {
- if (strcasecmp(record->record->rname, name) == 0)
- return 0;
+ if ((PICO_DNS_TYPE_NSEC == type) && 0 == strcasecmp(record->record->rname, name)) {
+ return 0;
}
}
}
/* If there is none present generate one for given name */
- if ((nsec = pico_mdns_gen_nsec_record(name)))
- pico_tree_insert(artree, nsec);
+ if ((nsec = pico_mdns_gen_nsec_record(name))) {
+ if (pico_tree_insert(artree, nsec)) {
+ pico_mdns_record_delete((void **)nsec);
+ return -1;
+ }
+ }
+
return 0;
}
@@ -2274,17 +2425,18 @@ pico_mdns_additionals_add_host( pico_mdns_rtree *artree )
struct pico_mdns_record *record = NULL, *copy = NULL;
pico_tree_foreach(node, &MyRecords) {
- if ((record = node->keyValue) &&
- IS_HOSTNAME_RECORD(record) &&
- IS_RECORD_VERIFIED(record)) {
- copy = pico_mdns_record_copy(record);
- if (copy && pico_tree_insert(artree, copy))
- pico_mdns_record_delete((void **)©);
+ record = node->keyValue;
+ if (record) {
+ if (IS_HOSTNAME_RECORD(record) && IS_RECORD_VERIFIED(record)) {
+ copy = pico_mdns_record_copy(record);
+ if (copy && pico_tree_insert(artree, copy))
+ pico_mdns_record_delete((void **)©);
+ }
}
}
return 0;
-}
+} /* Satic path count: 4 */
static void
pico_rtree_add_copy( pico_mdns_rtree *tree, struct pico_mdns_record *record )
@@ -2303,8 +2455,9 @@ pico_rtree_add_copy( pico_mdns_rtree *tree, struct pico_mdns_record *record )
}
/* ****************************************************************************
- * When a service is found additional records should be generated and added to
- * the either the answer section or the additional sections. This happens here
+ * When a service is found, additional records should be generated and
+ * added to either the answer section or the additional sections.
+ * This happens here
*
* @param antree mDNS record tree with answers to send
* @param artree mDNS record tree with additionals to send
@@ -2320,57 +2473,65 @@ pico_mdns_gather_service_meta( pico_mdns_rtree *antree,
char *sin = NULL, *service = NULL;
uint32_t ttl = 0;
- if (!antree || !artree || !srv_record) {
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
-
- /* Add hostname records */
- pico_mdns_additionals_add_host(artree);
-
/* Generate proper service instance name and service */
- if (!(sin = pico_dns_qname_to_url(srv_record->record->rname)))
+ sin = pico_dns_qname_to_url(srv_record->record->rname); // May be leaking
+
+ if (!antree || !artree || !sin) {
+ pico_err = PICO_ERR_EINVAL;
+ PICO_FREE(sin);
return -1;
+ } else {
+ /* Add hostname records */
+ pico_mdns_additionals_add_host(artree);
- service = sin + pico_dns_first_label_length(sin) + 1u;
- ttl = long_be(srv_record->record->rsuffix->rttl);
+ service = sin + pico_dns_first_label_length(sin) + 1u;
+ ttl = long_be(srv_record->record->rsuffix->rttl);
- /* Generate PTR records */
- ptr_record = pico_mdns_record_create(service, (void *)sin,
- (uint16_t)strlen(sin),
- PICO_DNS_TYPE_PTR,
- ttl, PICO_MDNS_RECORD_SHARED);
- /* Meta DNS-SD record */
- meta_record = pico_mdns_record_create("_services._dns-sd._udp.local",
- (void *)service,
- (uint16_t)strlen(service),
- PICO_DNS_TYPE_PTR,
- ttl, PICO_MDNS_RECORD_SHARED);
- PICO_FREE(sin);
- if (!meta_record || !ptr_record) {
- mdns_dbg("Could not generate META or PTR records!\n");
- pico_mdns_record_delete((void **)&ptr_record);
- pico_mdns_record_delete((void **)&meta_record);
- return -1;
+ /* Generate PTR records */
+ ptr_record = pico_mdns_record_create(service, (void *)sin,
+ (uint16_t)strlen(sin),
+ PICO_DNS_TYPE_PTR,
+ ttl, PICO_MDNS_RECORD_SHARED);
+ /* Meta DNS-SD record */
+ meta_record = pico_mdns_record_create("_services._dns-sd._udp.local",
+ (void *)service,
+ (uint16_t)strlen(service),
+ PICO_DNS_TYPE_PTR,
+ ttl, PICO_MDNS_RECORD_SHARED);
+ PICO_FREE(sin); // Free allocated memory
+ if (!meta_record || !ptr_record) {
+ mdns_dbg("Could not generate META or PTR records!\n");
+ pico_mdns_record_delete((void **)&ptr_record);
+ pico_mdns_record_delete((void **)&meta_record);
+ return -1;
+ }
+
+ ptr_record->flags |= (PICO_MDNS_RECORD_PROBED |
+ PICO_MDNS_RECORD_CLAIMED);
+ meta_record->flags |= (PICO_MDNS_RECORD_PROBED |
+ PICO_MDNS_RECORD_CLAIMED);
+
+ /* Add copies to the answer tree */
+ pico_rtree_add_copy(antree, meta_record);
+ pico_rtree_add_copy(antree, ptr_record);
+
+ /* Insert the created service record in MyRecords, alread in, destroy */
+ if (pico_tree_insert(&MyRecords, meta_record)) {
+ mdns_dbg("MDNS: Failed to insert meta record in tree\n");
+ pico_mdns_record_delete((void **)&meta_record);
+ pico_mdns_record_delete((void **)&ptr_record);
+ return -1;
+ }
+
+ if (pico_tree_insert(&MyRecords, ptr_record)) {
+ mdns_dbg("MDNS: Failed to insert ptr record in tree\n");
+ pico_mdns_record_delete((void **)&ptr_record);
+ pico_tree_delete(&MyRecords, meta_record);
+ pico_mdns_record_delete((void **)&meta_record);
+ }
}
-
- ptr_record->flags |= (PICO_MDNS_RECORD_PROBED | PICO_MDNS_RECORD_CLAIMED);
- meta_record->flags |= (PICO_MDNS_RECORD_PROBED | PICO_MDNS_RECORD_CLAIMED);
-
- /* Add copies to the answer tree */
- pico_rtree_add_copy(antree, meta_record);
- pico_rtree_add_copy(antree, ptr_record);
-
- /* Insert the created service record in MyRecords, if it's already in, destroy them */
- if (meta_record && pico_tree_insert(&MyRecords, meta_record)) {
- pico_mdns_record_delete((void **)&meta_record);
- }
- if (ptr_record && pico_tree_insert(&MyRecords, ptr_record)) {
- pico_mdns_record_delete((void **)&ptr_record);
- }
-
return 0;
-}
+} /* Static path count: 9 */
/* ****************************************************************************
* Gathers additional records for a to send response. Checks for services and
@@ -2392,34 +2553,34 @@ pico_mdns_gather_additionals( pico_mdns_rtree *antree,
if (!antree || !artree) {
pico_err = PICO_ERR_EINVAL;
return -1;
- }
+ } else {
+ /* Look for SRV records in the tree */
+ pico_tree_foreach(node, antree) {
+ if ((record = node->keyValue) &&
+ short_be(record->record->rsuffix->rtype) == PICO_DNS_TYPE_SRV &&
+ (ret = pico_mdns_gather_service_meta(antree, artree, record)))
+ return ret;
+ }
- /* Look for SRV records in the answer tree */
- pico_tree_foreach(node, antree) {
- if ((record = node->keyValue) &&
- short_be(record->record->rsuffix->rtype) == PICO_DNS_TYPE_SRV &&
- (ret = pico_mdns_gather_service_meta(antree, artree, record)))
- return ret;
- }
+ /* Look for unique records in the tree to generate NSEC records */
+ pico_tree_foreach(node, antree) {
+ if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) &&
+ (ret = pico_mdns_additionals_add_nsec(artree,
+ record->record->rname)))
+ return ret;
+ }
- /* Look for unique records in the answer tree to generate NSEC records */
- pico_tree_foreach(node, antree) {
- if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) &&
- (ret = pico_mdns_additionals_add_nsec(artree,
- record->record->rname)))
- return ret;
- }
-
- /* Look for unique records in the additional tree to generate NSEC records*/
- pico_tree_foreach(node, artree) {
- if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) &&
- (ret = pico_mdns_additionals_add_nsec(artree,
- record->record->rname)))
- return ret;
+ /* Look for unique records in the additionals to generate NSEC records*/
+ pico_tree_foreach(node, artree) {
+ if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) &&
+ (ret = pico_mdns_additionals_add_nsec(artree,
+ record->record->rname)))
+ return ret;
+ }
}
return 0;
-}
+} /* Static path count: 9 */
/* ****************************************************************************
* Sends mDNS records to either multicast peer via unicast to a single peer.
@@ -2600,7 +2761,7 @@ pico_mdns_handle_probe_packet( pico_dns_packet *packet, struct pico_ip4 peer )
pico_mdns_handle_data_as_answers_generic(&data, nscount, packet, 1);
/* Try to reply with the answers */
- if (pico_tree_count(&antree) != 0){
+ if (pico_tree_count(&antree) != 0) {
int retval = pico_mdns_reply(&antree, peer);
PICO_MDNS_RTREE_DESTROY(&antree);
return retval;
@@ -2652,42 +2813,49 @@ pico_mdns_recv( void *buf, int buflen, struct pico_ip4 peer )
uint16_t authcount = short_be(packet->nscount);
uint16_t addcount = short_be(packet->arcount);
- mdns_dbg(">>>>>>> QDcount: %u, ANcount: %u, NScount: %u, ARcount: %u\n",
- qdcount, ancount, authcount, addcount);
+ /* RFC6762: */
+ /* 18.3: Messages received with an opcode other than zero MUST be silently */
+ /* ignored. */
+ /* 18.11: messages received with non-zero Response Codes MUST be silently */
+ /* ignored */
+ if(packet->opcode == 0 && packet->rcode == 0) {
+ mdns_dbg(">>>>>>> QDcount: %u, ANcount: %u, NScount: %u, ARcount: %u\n",
+ qdcount, ancount, authcount, addcount);
- IGNORE_PARAMETER(buflen);
- IGNORE_PARAMETER(addcount);
+ IGNORE_PARAMETER(buflen);
+ IGNORE_PARAMETER(addcount);
- /* DNS PACKET TYPE DETERMINATION */
- if ((qdcount > 0)) {
- if (authcount > 0) {
- mdns_dbg(">>>>>>> RCVD a mDNS probe query:\n");
- /* Packet is probe query */
- if (pico_mdns_handle_probe_packet(packet, peer) < 0) {
- mdns_dbg("Could not handle mDNS probe query!\n");
- return -1;
+ /* DNS PACKET TYPE DETERMINATION */
+ if ((qdcount > 0)) {
+ if (authcount > 0) {
+ mdns_dbg(">>>>>>> RCVD a mDNS probe query:\n");
+ /* Packet is probe query */
+ if (pico_mdns_handle_probe_packet(packet, peer) < 0) {
+ mdns_dbg("Could not handle mDNS probe query!\n");
+ return -1;
+ }
+ } else {
+ mdns_dbg(">>>>>>> RCVD a plain mDNS query:\n");
+ /* Packet is a plain query */
+ if (pico_mdns_handle_query_packet(packet, peer) < 0) {
+ mdns_dbg("Could not handle plain DNS query!\n");
+ return -1;
+ }
}
} else {
- mdns_dbg(">>>>>>> RCVD a plain mDNS query:\n");
- /* Packet is a plain query */
- if (pico_mdns_handle_query_packet(packet, peer) < 0) {
- mdns_dbg("Could not handle plain DNS query!\n");
+ if (ancount > 0) {
+ mdns_dbg(">>>>>>> RCVD a mDNS response:\n");
+ /* Packet is a response */
+ if (pico_mdns_handle_response_packet(packet) < 0) {
+ mdns_dbg("Could not handle DNS response!\n");
+ return -1;
+ }
+ } else {
+ /* Something went wrong here... */
+ mdns_dbg("RCVD Packet contains no questions or answers...\n");
return -1;
}
}
- } else {
- if (ancount > 0) {
- mdns_dbg(">>>>>>> RCVD a mDNS response:\n");
- /* Packet is a response */
- if (pico_mdns_handle_response_packet(packet) < 0) {
- mdns_dbg("Could not handle DNS response!\n");
- return -1;
- }
- } else {
- /* Something went wrong here... */
- mdns_dbg("RCVD Packet contains no questions or answers...\n");
- return -1;
- }
}
return 0;
@@ -2750,7 +2918,6 @@ pico_mdns_send_query_packet( pico_time now, void *arg )
IGNORE_PARAMETER(now);
/* Parse in the cookie */
- cookie = (struct pico_mdns_cookie *)arg;
if (!cookie || cookie->type != PICO_MDNS_PACKET_TYPE_QUERY)
return;
@@ -2763,6 +2930,11 @@ pico_mdns_send_query_packet( pico_time now, void *arg )
packet->id = 0;
+ /* RFC6762: 18.6: In both multicast query and response messages,
+ the RD bit SHOULD be zero on transmission. In pico_dns_fill_packet_header,
+ the RD bit is set to PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
+ packet->rd = PICO_DNS_RD_NO_DESIRE;
+
if (cookie->status != PICO_MDNS_COOKIE_STATUS_CANCELLED) {
cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
if(pico_mdns_send_packet(packet, len) != (int)len) {
@@ -2776,8 +2948,9 @@ pico_mdns_send_query_packet( pico_time now, void *arg )
pico_timer_cancel(cookie->send_timer);
/* Remove cookie from Cookies */
cookie = pico_tree_delete(&Cookies, cookie);
- pico_mdns_cookie_delete(&cookie);
+ pico_mdns_cookie_delete((void **)&cookie);
}
+
PICO_FREE(packet);
}
@@ -2809,7 +2982,13 @@ pico_mdns_getrecord_generic( const char *url, uint16_t type,
mdns_dbg("question_create returned NULL!\n");
return -1;
}
- pico_tree_insert(&qtree, q);
+
+ if (pico_tree_insert(&qtree, q)) {
+ mdns_dbg("inserting query into tree failed!\n");
+ pico_dns_question_delete((void **)&q);
+ return -1;
+ }
+
/* Create a mDNS cookie to send */
if (!(cookie = pico_mdns_cookie_create(qtree, antree, artree, 1,
@@ -2821,10 +3000,23 @@ pico_mdns_getrecord_generic( const char *url, uint16_t type,
}
/* Add cookie to Cookies to be able to find it afterwards */
- pico_tree_insert(&Cookies, cookie);
+ if(pico_tree_insert(&Cookies, cookie) ){
+ mdns_dbg("inserting cookie into tree failed!\n");
+ PICO_DNS_QTREE_DESTROY(&qtree);
+ pico_mdns_cookie_delete((void **)&cookie);
+ return -1;
+ }
+
/* Create new pico_timer-event to send packet */
- pico_timer_add((pico_rand() % 120) + 20, pico_mdns_send_query_packet,
- (void *)cookie);
+ if (!pico_mdns_timer_add((pico_rand() % 120) + 20, pico_mdns_send_query_packet,
+ (void *)cookie)) {
+ mdns_dbg("MDNS: Failed to start send_query_packet timer\n");
+ pico_tree_delete(&Cookies, cookie);
+ pico_mdns_cookie_delete((void**)&cookie);
+ pico_dns_question_delete((void**)&q);
+ return -1;
+ }
+
return 0;
}
@@ -2834,7 +3026,7 @@ pico_mdns_getrecord_generic( const char *url, uint16_t type,
* the wire for this record.
*
* @param url URL to query for.
- * @param type DNS type top query for.
+ * @param type DNS type to 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.
* ****************************************************************************/
@@ -2870,9 +3062,8 @@ pico_mdns_getrecord( const char *url, uint16_t type,
return pico_mdns_getrecord_generic(url, type, callback, arg);
#if PICO_MDNS_ALLOW_CACHING == 1
}
+return 0;
#endif
-
- return 0;
}
/* MARK: PROBING & ANNOUNCING */
@@ -2883,7 +3074,7 @@ pico_mdns_getrecord( const char *url, uint16_t type,
*
* @param now Ignore
* @param arg Void-pointer to mDNS announcement cookie
- * ****************************************************************************/
+ * ***************************************************************************/
static void
pico_mdns_send_announcement_packet( pico_time now, void *arg )
{
@@ -2891,8 +3082,9 @@ pico_mdns_send_announcement_packet( pico_time now, void *arg )
/* Check params */
IGNORE_PARAMETER(now);
- if (!cookie)
+ if (!cookie) {
return;
+ }
cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
if (cookie->count > 0) {
@@ -2900,8 +3092,15 @@ pico_mdns_send_announcement_packet( pico_time now, void *arg )
pico_mdns_reply(&(cookie->antree), inaddr_any);
mdns_dbg("DONE - Sent announcement!\n");
- /* The host needs to send 2 announcements 1 second apart */
- if (--(cookie->count) == 0) {
+ /* The Multicast DNS responder MUST send at least two unsolicited
+ responses, one second apart. To provide increased robustness
+ against packet loss, a responder MAY send up to eight unsolicited
+ responses, provided that the interval between unsolicited
+ responses increases by at least a factor of two with
+ every response sent.
+ */
+ --(cookie->count);
+ if (cookie->count == 0) {
cookie->status = PICO_MDNS_COOKIE_STATUS_INACTIVE;
/* Update the states of the records */
@@ -2911,10 +3110,24 @@ pico_mdns_send_announcement_packet( pico_time now, void *arg )
/* Try to delete the cookie */
pico_tree_delete(&Cookies, cookie);
- pico_mdns_cookie_delete(&cookie);
+ pico_mdns_cookie_delete((void **)&cookie);
+ }
+ else{
+ /*
+ A responder MAY send up to eight unsolicited responses,
+ provided that the interval between unsolicited responses increases
+ by at least a factor of two with every response sent.
+ Starting at 1 second.
+ So we bithsift to get our powers of two and we multiply by 1000 to
+ get our miliseconds.
+ */
+ if (!pico_mdns_timer_add((pico_time)((1 << (PICO_MDNS_ANNOUNCEMENT_COUNT - cookie->count - 1))
+ * 1000), pico_mdns_send_announcement_packet, cookie)) {
+ mdns_dbg("MDNS: Failed to start send_announcement_packet timer\n");
+ /* TODO no idea what the consequences of this are */
+
+ }
}
- else /* Announcement should be sent with a delay of 1s in between. */
- pico_timer_add(1000, pico_mdns_send_announcement_packet, cookie);
}
}
@@ -2946,8 +3159,9 @@ pico_mdns_announce( void (*callback)(pico_mdns_rtree *,
/* Find out which resource records can be announced */
antree = pico_mdns_my_records_find_probed();
- if (pico_tree_count(&antree) == 0)
+ if (pico_tree_count(&antree) == 0) {
return 0;
+ }
/* Create a mDNS packet cookie */
if (!(announcement_cookie = pico_mdns_cookie_create(qtree, antree, artree,
@@ -2987,7 +3201,10 @@ pico_mdns_gen_probe_auths( pico_mdns_rtree *records )
/* Clear the cache flush bit for authority records in probes */
PICO_MDNS_CLR_MSB_BE(record->record->rsuffix->rclass);
/* Only the actual DNS records is required */
- pico_tree_insert(&nstree, record->record);
+ if (pico_tree_insert(&nstree, record->record) == &LEAF) {
+ mdns_dbg("MDNS: Failed to insert record in tree\n");
+ break;
+ }
}
}
@@ -2995,7 +3212,7 @@ pico_mdns_gen_probe_auths( pico_mdns_rtree *records )
}
/* ****************************************************************************
- * Function to create a probe form an mDNS cookie and send it on the wire.
+ * Function to create a probe from an mDNS cookie and send it on the wire.
*
* @param now Ignore
* @param arg Void-pointer to mDNS probe cookie
@@ -3010,54 +3227,71 @@ pico_mdns_send_probe_packet( pico_time now, void *arg )
/* Check params */
IGNORE_PARAMETER(now);
- if (!cookie || (cookie->type == PICO_MDNS_COOKIE_STATUS_INACTIVE)) {
+ /* if (!cookie || (cookie->type == PICO_MDNS_COOKIE_STATUS_INACTIVE)) { */
+ if (!cookie || (cookie->type != PICO_MDNS_PACKET_TYPE_PROBE)) {
pico_err = PICO_ERR_EINVAL;
return;
- }
-
- /* Set the cookie to the active state */
- cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
- if (cookie->count-- > 0) {
- /* Generate authority records */
- nstree = pico_mdns_gen_probe_auths(&(cookie->antree));
-
- /* Create an mDNS answer */
- if (!(packet = pico_dns_query_create(&(cookie->qtree), NULL,
- &nstree, NULL, &len))) {
- PICO_DNS_RTREE_DESTROY(&nstree);
- mdns_dbg("Could not create probe packet!\n");
- return;
- }
- pico_tree_destroy(&nstree, NULL);
-
- /* Send the mDNS answer unsolicited via multicast */
- if(pico_mdns_send_packet(packet, len) != (int)len) {
- mdns_dbg("Send error occurred!\n");
- return;
- }
- PICO_FREE(packet);
-
- mdns_dbg("DONE - Sent probe!\n");
-
- /* Probes should be sent with a delay in between of 250 ms */
- if (PICO_MDNS_COOKIE_STATUS_ACTIVE == cookie->status )
- cookie->send_timer = pico_timer_add(250,
- pico_mdns_send_probe_packet,
- (void *)cookie);
} else {
- mdns_dbg("DONE - Probing.\n");
+ /* Set the cookie to the active state */
+ cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
+ if (cookie->count > 0) {
+ --(cookie->count);
- pico_mdns_my_records_probed(&(cookie->antree));
+ /* Generate authority records */
+ nstree = pico_mdns_gen_probe_auths(&(cookie->antree));
- /* Start announcing */
- cookie->count = PICO_MDNS_ANNOUNCEMENT_COUNT;
- cookie->type = PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT;
- pico_mdns_send_announcement_packet(0, (void*) cookie);
+ /* Create an mDNS answer */
+ if (!(packet = pico_dns_query_create(&(cookie->qtree), NULL,
+ &nstree, NULL, &len))) {
+ PICO_DNS_RTREE_DESTROY(&nstree);
+ mdns_dbg("Could not create probe packet!\n");
+ return;
+ }
+
+ pico_tree_destroy(&nstree, NULL);
+
+ /* RFC6762: 18.1 */
+ packet->id = 0;
+
+ /* RFC6762: 18.6: In both multicast query and response messages,
+ the RD bit SHOULD be zero on transmission.
+ In pico_dns_fill_packet_header, the RD bit is set to
+ PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
+ packet->rd = PICO_DNS_RD_NO_DESIRE;
+
+ /* Send the mDNS answer unsolicited via multicast */
+ if(pico_mdns_send_packet(packet, len) != (int)len) {
+ mdns_dbg("Send error occurred!\n");
+ return;
+ }
+
+ PICO_FREE(packet);
+
+ mdns_dbg("DONE - Sent probe!\n");
+
+ /* Probes should be sent with a delay in between of 250 ms */
+ if (PICO_MDNS_COOKIE_STATUS_ACTIVE == cookie->status ) {
+ cookie->send_timer = pico_mdns_timer_add(250,
+ pico_mdns_send_probe_packet,
+ (void *)cookie);
+ if (!cookie->send_timer) {
+ mdns_dbg("MDNS: Failed to start send_probe_packet timer\n");
+ /* TODO no idea what the consequences of this are */
+ return;
+ }
+ }
+ } else {
+ mdns_dbg("DONE - Probing.\n");
+
+ pico_mdns_my_records_probed(&(cookie->antree));
+
+ /* Start announcing */
+ cookie->count = PICO_MDNS_ANNOUNCEMENT_COUNT;
+ cookie->type = PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT;
+ pico_mdns_send_announcement_packet(0, (void*) cookie);
+ }
}
-
-
- return;
-}
+} /* Static path count: 10 */
/* ****************************************************************************
* Adds a new probe question to the probe cookie questions, if a probe question
@@ -3123,45 +3357,56 @@ static int pico_mdns_probe( void (*callback)(pico_mdns_rtree *,
if (!callback) {
pico_err = PICO_ERR_EINVAL;
return -1;
- }
+ } else {
+ /* Find my records that need to pass the probing step first
+ * All records that don't have their PROBED flag set and
+ * are not being probed at hte moment are added to the tree
+ */
+ antree = pico_mdns_my_records_find_to_probe();
- /* Find my records that need to pass the probing step first */
- antree = pico_mdns_my_records_find_to_probe();
-
- /* Crate probe questions for the to probe records */
- pico_tree_foreach(node, &antree) {
- if ((record = node->keyValue)) {
- pico_mdns_add_probe_question(&qtree, record->record->rname);
+ /* Create probe questions for the records to be probed */
+ pico_tree_foreach(node, &antree) {
+ if ((record = node->keyValue)) {
+ pico_mdns_add_probe_question(&qtree, record->record->rname);
+ }
}
+
+ /* Create a mDNS packet to send */
+ cookie = pico_mdns_cookie_create(qtree, antree, artree,
+ PICO_MDNS_PROBE_COUNT,
+ PICO_MDNS_PACKET_TYPE_PROBE,
+ callback, arg);
+ if (!cookie) {
+ mdns_dbg("Cookie_create returned NULL @ probe()!\n");
+ PICO_DNS_QTREE_DESTROY(&qtree);
+ PICO_MDNS_RTREE_DESTROY(&antree);
+ return -1;
+ }
+
+ /* Add the probe cookie to the cookie tree */
+ if (pico_tree_insert(&Cookies, cookie)) {
+ pico_mdns_cookie_delete((void **)&cookie);
+ return -1;
+ }
+
+ /* RFC6762: 8.1. Probing */
+ /* When ready to send its Multicast DNS probe packet(s) the host should */
+ /* first wait for a short random delay time, uniformly distributed in */
+ /* the range 0-250 ms. */
+ cookie->send_timer = pico_mdns_timer_add(pico_rand() % 250,
+ pico_mdns_send_probe_packet,
+ (void *)cookie);
+ if (!cookie->send_timer) {
+ mdns_dbg("MDNS: Failed to start send_probe_packet timer\n");
+ pico_tree_delete(&Cookies, cookie);
+ pico_mdns_cookie_delete((void**)&cookie);
+ return -1;
+ }
+
+ mdns_dbg("DONE - Started probing.\n");
}
-
- /* Create a mDNS packet to send */
- cookie = pico_mdns_cookie_create(qtree, antree, artree,
- PICO_MDNS_PROBE_COUNT,
- PICO_MDNS_PACKET_TYPE_PROBE,
- callback, arg);
- if (!cookie) {
- mdns_dbg("Cookie_create returned NULL @ probe()!\n");
- PICO_DNS_QTREE_DESTROY(&qtree);
- PICO_MDNS_RTREE_DESTROY(&antree);
- return -1;
- }
-
- /* Add the probe cookie to the cookie tree */
- if (pico_tree_insert(&Cookies, cookie)) {
- pico_mdns_cookie_delete(&cookie);
- return -1;
- }
-
- /* When the host is ready to send his probe query he SHOULD delay it's
- transmission with a randomly chosen time between 0 and 250 ms. */
- cookie->send_timer = pico_timer_add(pico_rand() % 250,
- pico_mdns_send_probe_packet,
- (void *)cookie);
- mdns_dbg("DONE - Started probing.\n");
-
return 0;
-}
+} /* Static path count: 9 */
/* MARK: API functions */
@@ -3222,7 +3467,7 @@ pico_mdns_claim( pico_mdns_rtree rtree,
void *),
void *arg )
{
- return pico_mdns_claim_generic(rtree, 0, callback, arg);
+ return pico_mdns_claim_generic(rtree, PICO_MDNS_NO_RECLAIM, callback, arg);
}
/* ****************************************************************************
@@ -3240,71 +3485,66 @@ pico_mdns_reclaim( pico_mdns_rtree rtree,
void *),
void *arg )
{
- return pico_mdns_claim_generic(rtree, 1, callback, arg);
+ return pico_mdns_claim_generic(rtree, PICO_MDNS_RECLAIM, callback, 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
+ * Tries to claim a hostname for this machine. Claims automatically a
+ * unique A record with the IPv4-address of this host.
+ * The hostname won't be set directly when this functions returns,
+ * but only if the claiming of the unique record succeeded.
+ * Init-callback will be called when the hostname-record is successfully
* registered.
*
* @param url URL to set the hostname to.
* @param arg Argument to pass to the init-callback.
* @return 0 when the host started registering the hostname-record successfully,
- * Returns something else when it didn't succeeded.
+ * Returns something else when it didn't succeeded.
* ****************************************************************************/
int
-pico_mdns_set_hostname( const char *url, void *arg )
+pico_mdns_tryclaim_hostname( const char *url, void *arg )
{
PICO_MDNS_RTREE_DECLARE(rtree);
struct pico_mdns_record *record = NULL;
- /* Check params */
- if (!url) {
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
-
/* Check if module is initialised */
if (!mdns_sock_ipv4) {
mdns_dbg("mDNS socket not initialised, did you call 'pico_mdns_init()'?\n");
pico_err = PICO_ERR_EINVAL;
return -1;
+ } else {
+ /* Create an A record for hostname */
+ record = pico_mdns_record_create(url,
+ &(mdns_sock_ipv4->local_addr.ip4.addr),
+ PICO_SIZE_IP4, PICO_DNS_TYPE_A,
+ PICO_MDNS_DEFAULT_TTL,
+ (PICO_MDNS_RECORD_UNIQUE |
+ PICO_MDNS_RECORD_HOSTNAME));
+ if (!record) {
+ mdns_dbg("Could not create A record for hostname %s!\n",
+ strerror(pico_err));
+ return -1;
+ }
+
+ /* TODO: Create IPv6 record */
+ /* TODO: Create a reverse resolution record */
+
+ /* Try to claim the record */
+ if (pico_tree_insert(&rtree, record)) {
+ pico_mdns_record_delete((void **)&record);
+ return -1;
+ }
+
+ if (pico_mdns_claim(rtree, init_callback, arg)) {
+ mdns_dbg("Could not claim record for hostname %s!\n", url);
+ PICO_MDNS_RTREE_DESTROY(&rtree);
+ return -1;
+ }
+
+ pico_tree_destroy(&rtree, NULL);
}
-
- /* Create an A record for hostname */
- record = pico_mdns_record_create(url,
- &(mdns_sock_ipv4->local_addr.ip4.addr),
- PICO_SIZE_IP4, PICO_DNS_TYPE_A,
- PICO_MDNS_DEFAULT_TTL,
- (PICO_MDNS_RECORD_UNIQUE |
- PICO_MDNS_RECORD_HOSTNAME));
- if (!record) {
- mdns_dbg("Could not create A record for hostname %s!\n",
- strerror(pico_err));
- return -1;
- }
-
- /* TODO: Create IPv6 record */
- /* TODO: Create a reverse resolution record */
-
- /* Try to claim the record */
- if (pico_tree_insert(&rtree, record)) {
- pico_mdns_record_delete((void **)&record);
- return -1;
- }
-
- if (pico_mdns_claim(rtree, init_callback, arg)) {
- mdns_dbg("Could not claim record for hostname %s!\n", url);
- PICO_MDNS_RTREE_DESTROY(&rtree);
- return -1;
- }
- pico_tree_destroy(&rtree, NULL);
-
return 0;
-}
+} /* Static path count: 9 */
/* ****************************************************************************
* Get the hostname for this machine.
@@ -3325,6 +3565,25 @@ pico_mdns_get_hostname( void )
return (const char *)_hostname;
}
+static void
+pico_mdns_cleanup( void )
+{
+ /* Delete socket if it was previously opened */
+ if (mdns_sock_ipv4) {
+ pico_socket_del(mdns_sock_ipv4);
+ }
+
+ /* Clear out every memory structure used by mDNS */
+#if PICO_MDNS_ALLOW_CACHING == 1
+ PICO_MDNS_RTREE_DESTROY(&Cache);
+#endif /* PICO_MDNS_ALLOW_CACHING */
+ PICO_MDNS_RTREE_DESTROY(&MyRecords);
+ PICO_MDNS_CTREE_DESTROY(&Cookies);
+
+ /* Cancel every timer */
+ pico_timer_cancel_hashed(mdns_hash);
+}
+
/* ****************************************************************************
* Initialises the entire mDNS-module and sets the hostname for this machine.
* Sets up the global mDNS socket properly and calls callback when succeeded.
@@ -3363,6 +3622,13 @@ pico_mdns_init( const char *hostname,
return -1;
}
+ /* Clear out all the memory structure's and delete socket if it was
+ * already opened before */
+ pico_mdns_cleanup();
+
+ /* Create a hash to identify mDNS timers with */
+ mdns_hash = pico_hash(hostname, (uint32_t)strlen(hostname));
+
/* Open global IPv4 mDNS socket */
mdns_sock_ipv4 = pico_socket_open(proto4, PICO_PROTO_UDP, &pico_mdns_event4);
if(!mdns_sock_ipv4) {
@@ -3391,9 +3657,10 @@ pico_mdns_init( const char *hostname,
return -1;
}
- /* RFC:
- * All multicast responses (including answers sent via unicast) SHOULD
- * be send with IP TTL set to 255 for backward-compatibility reasons
+ /* RFC6762:
+ * 11. Source Address Check
+ * All Multicast DNS responses (including responses sent via unicast)
+ * SHOULD be sent with IP TTL set to 255.
*/
if(pico_socket_setoption(mdns_sock_ipv4, PICO_IP_MULTICAST_TTL, &ttl)) {
mdns_dbg("socket_setoption PICO_IP_MULTICAST_TTL failed\n");
@@ -3408,10 +3675,13 @@ pico_mdns_init( const char *hostname,
/* Set the global init callback variable */
init_callback = callback;
- pico_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL);
+ if (!pico_mdns_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL)) {
+ mdns_dbg("MDNS: Failed to start tick timer\n");
+ return -1;
+ }
/* Set the hostname eventually */
- return pico_mdns_set_hostname(hostname, arg);
+ return pico_mdns_tryclaim_hostname(hostname, arg);
}
#endif /* PICO_SUPPORT_MDNS */
diff --git a/ext/picotcp/modules/pico_mdns.h b/ext/picotcp/modules/pico_mdns.h
index d4e6a4e..54c8ede 100644
--- a/ext/picotcp/modules/pico_mdns.h
+++ b/ext/picotcp/modules/pico_mdns.h
@@ -1,6 +1,6 @@
/* ****************************************************************************
* PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved.
- * See LICENSE and COPYING for usage.
+ * See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
* .
* Author: Toon Stegen, Jelle De Vleeschouwer
* ****************************************************************************/
@@ -17,8 +17,24 @@
#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_PROBE_COUNT 3
+/* Amount of probes to send:
+ RFC6762: 8.1. Probing:
+ 250 ms after the first query, the host should send a second; then,
+ 250 ms after that, a third. If, by 250 ms after the third probe, no
+ conflicting Multicast DNS responses have been received, the host may
+ move to the next step, announcing.
+ */
+
+#define PICO_MDNS_ANNOUNCEMENT_COUNT 3
+/* Amount of announcements to send: (we've opted for 1 extra for robustness)
+ RFC6762: 8.3. Announcing:
+ The Multicast DNS responder MUST send at least two unsolicited
+ responses, one second apart. To provide increased robustness against
+ packet loss, a responder MAY send up to eight unsolicited responses,
+ provided that the interval between unsolicited responses increases by
+ at least a factor of two with every response sent.
+ */
/* ****************************************************************************/
#define PICO_MDNS_DEST_ADDR4 "224.0.0.251"
@@ -27,6 +43,10 @@
#define PICO_MDNS_RECORD_UNIQUE 0x00u
#define PICO_MDNS_RECORD_SHARED 0x01u
+/* To indicate if we reclaim or not */
+#define PICO_MDNS_RECLAIM 1
+#define PICO_MDNS_NO_RECLAIM 0
+
/* Flag to check for when records are returned, to determine the hostname */
#define PICO_MDNS_RECORD_HOSTNAME 0x02u
#define IS_HOSTNAME_RECORD(x) \
@@ -134,19 +154,20 @@ pico_mdns_claim( pico_mdns_rtree record_tree,
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
+ * Tries to claim a hostname for this machine. Claims automatically a
+ * unique A record with the IPv4-address of this host.
+ * The hostname won't be set directly when this functions returns,
+ * but only if the claiming of the unique record succeeded.
+ * Init-callback will be called when the hostname-record is successfully
* registered.
*
* @param url URL to set the hostname to.
* @param arg Argument to pass to the init-callback.
* @return 0 when the host started registering the hostname-record successfully,
- * Returns something else when it didn't succeeded.
+ * Returns something else when it didn't succeeded.
* ****************************************************************************/
int
-pico_mdns_set_hostname( const char *url, void *arg );
+pico_mdns_tryclaim_hostname( const char *url, void *arg );
/* ****************************************************************************
* Get the current hostname for this machine.
diff --git a/ext/picotcp/modules/pico_mld.c b/ext/picotcp/modules/pico_mld.c
index 67dd11f..c362acf 100644
--- a/ext/picotcp/modules/pico_mld.c
+++ b/ext/picotcp/modules/pico_mld.c
@@ -1,8 +1,8 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- See LICENSE and COPYING for usage.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
- RFC 2710 3019 3590 3810 4604 6636
+ RFC 2710 3019 3590 3810 4604 6636
Authors: Roel Postelmans
*********************************************************************/
@@ -21,40 +21,46 @@
#include "pico_dns_client.h"
#include "pico_mld.h"
#include "pico_constants.h"
+#include "pico_mcast.h"
-#if defined(PICO_SUPPORT_MLD) && defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_MCAST)
+#if defined(PICO_SUPPORT_MLD) && defined(PICO_SUPPORT_IPV6) && defined(PICO_SUPPORT_MCAST)
+#ifdef DEBUG_MLD
+#define mld_dbg dbg
+#else
#define mld_dbg(...) do {} while(0)
+#endif
+
/* MLD groups */
-#define MLD_ALL_HOST_GROUP "FF01:0:0:0:0:0:0:1"
+#define MLD_ALL_HOST_GROUP "FF01:0:0:0:0:0:0:1"
#define MLD_ALL_ROUTER_GROUP "FF01:0:0:0:0:0:0:2"
#define MLDV2_ALL_ROUTER_GROUP "FF02:0:0:0:0:0:0:16"
#define MLD_ROUTER_ALERT_LEN (8)
-
-uint8_t pico_mld_flag = 0;
+
+static uint8_t pico_mld_flag = 0;
PACKED_STRUCT_DEF mld_message {
- uint8_t type;
- uint8_t code;
- uint16_t crc;
- uint16_t max_resp_delay;
- uint16_t reserved;
- struct pico_ip6 mcast_group;
+ uint8_t type;
+ uint8_t code;
+ uint16_t crc;
+ uint16_t max_resp_delay;
+ uint16_t reserved;
+ struct pico_ip6 mcast_group;
};
PACKED_STRUCT_DEF mldv2_group_record {
uint8_t type;
uint8_t aux;
uint16_t nbr_src;
struct pico_ip6 mcast_group;
- struct pico_ip6 src[0];
+ struct pico_ip6 src[1];
};
PACKED_STRUCT_DEF mldv2_report {
- uint8_t type;
- uint8_t res;
- uint16_t crc;
+ uint8_t type;
+ uint8_t res;
+ uint16_t crc;
uint16_t res1;
uint16_t nbr_gr;
- struct mldv2_group_record record[0];
+ struct mldv2_group_record record[1];
};
PACKED_STRUCT_DEF mldv2_query {
uint8_t type;
@@ -66,47 +72,54 @@ PACKED_STRUCT_DEF mldv2_query {
uint8_t rsq;
uint8_t qqic;
uint16_t nbr_src;
- struct pico_ip6 source_addr[0];
+ struct pico_ip6 source_addr[1];
};
-typedef int (*mld_callback) (struct mld_parameters *);
-static int pico_mld_process_event(struct mld_parameters *p);
-static struct mld_parameters *pico_mld_find_parameter(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group);
+typedef int (*mld_callback)(struct mcast_parameters *);
+static int pico_mld_process_event(struct mcast_parameters *p);
+static struct mcast_parameters *pico_mld_find_parameter(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group);
-static uint8_t *pico_mld_fill_hopbyhop(struct pico_ipv6_hbhoption *hbh) {
+static uint8_t *pico_mld_fill_hopbyhop(struct pico_ipv6_hbhoption *hbh)
+{
uint8_t *p;
if(hbh == NULL)
return NULL;
- hbh->type = PICO_PROTO_ICMP6;
- hbh->len=0;
- // ROUTER ALERT, RFC2711
+
+ hbh->type = PICO_PROTO_ICMP6;
+ hbh->len = 0;
+ /* ROUTER ALERT, RFC2711 */
p = (uint8_t *)hbh + sizeof(struct pico_ipv6_hbhoption);
*(p++) = PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT;
- *(p++) = PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT_DATALEN;
- *(p++) = 0;
+ *(p++) = PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT_DATALEN;
*(p++) = 0;
- //PadN allignment with N=2
+ *(p++) = 0;
+ /* PadN allignment with N=2 */
*(p++) = 1;
*(p++) = 0; /* N-2 */
- return p;
+ return p;
}
-static int pico_mld_check_hopbyhop(struct pico_ipv6_hbhoption *hbh) {
- uint8_t options[8] = { PICO_PROTO_ICMP6, 0, PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT,\
- PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT_DATALEN,0,0,1,0 };
+static int pico_mld_check_hopbyhop(struct pico_ipv6_hbhoption *hbh)
+{
+ uint8_t options[8] = {
+ PICO_PROTO_ICMP6, 0, PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT, \
+ PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT_DATALEN, 0, 0, 1, 0
+ };
int i;
uint8_t *p;
if(hbh == NULL)
return -1;
+
if(hbh->type != options[0] || hbh->len != options[1])
return -1;
p = (uint8_t *)hbh + sizeof(struct pico_ipv6_hbhoption);
- for(i=0; itype < b->type)
return -1;
@@ -115,14 +128,17 @@ static inline int mldt_type_compare(struct mld_timer *a, struct mld_timer *b) {
return 0;
}
-static inline int mldt_group_compare(struct mld_timer *a, struct mld_timer *b) {
+static inline int mldt_group_compare(struct mld_timer *a, struct mld_timer *b)
+{
return pico_ipv6_compare(&a->mcast_group, &b->mcast_group);
}
-static inline int mldt_link_compare(struct mld_timer *a, struct mld_timer *b) {
+static inline int mldt_link_compare(struct mld_timer *a, struct mld_timer *b)
+{
return pico_ipv6_compare(&a->mcast_link, &b->mcast_link);
}
-static int mld_timer_cmp(void *ka, void *kb) {
+static int mld_timer_cmp(void *ka, void *kb)
+{
struct mld_timer *a = ka, *b = kb;
int cmp = mldt_type_compare(a, b);
if (cmp)
@@ -134,8 +150,9 @@ static int mld_timer_cmp(void *ka, void *kb) {
return mldt_link_compare(a, b);
}
-static void pico_mld_report_expired(struct mld_timer *t) {
- struct mld_parameters *p = NULL;
+static void pico_mld_report_expired(struct mld_timer *t)
+{
+ struct mcast_parameters *p = NULL;
p = pico_mld_find_parameter(&t->mcast_link, &t->mcast_group);
if (!p)
@@ -144,7 +161,7 @@ static void pico_mld_report_expired(struct mld_timer *t) {
p->event = MLD_EVENT_TIMER_EXPIRED;
pico_mld_process_event(p);
}
-PICO_TREE_DECLARE(MLDTimers, mld_timer_cmp);
+static PICO_TREE_DECLARE(MLDTimers, mld_timer_cmp);
static void pico_mld_v1querier_expired(struct mld_timer *t)
{
struct pico_ipv6_link *link = NULL;
@@ -168,14 +185,17 @@ static void pico_mld_v1querier_expired(struct mld_timer *t)
}
-static inline int mldparm_group_compare(struct mld_parameters *a, struct mld_parameters *b) {
- return pico_ipv6_compare(&a->mcast_group, &b->mcast_group);
+static inline int mldparm_group_compare(struct mcast_parameters *a, struct mcast_parameters *b)
+{
+ return pico_ipv6_compare(&a->mcast_group.ip6, &b->mcast_group.ip6);
}
-static inline int mldparm_link_compare(struct mld_parameters *a, struct mld_parameters *b) {
- return pico_ipv6_compare(&a->mcast_link, &b->mcast_link);
+static inline int mldparm_link_compare(struct mcast_parameters *a, struct mcast_parameters *b)
+{
+ return pico_ipv6_compare(&a->mcast_link.ip6, &b->mcast_link.ip6);
}
-static int mld_parameters_cmp(void *ka, void *kb) {
- struct mld_parameters *a = ka, *b = kb;
+static int mcast_parameters_cmp(void *ka, void *kb)
+{
+ struct mcast_parameters *a = ka, *b = kb;
int cmp = mldparm_group_compare(a, b);
if (cmp)
return cmp;
@@ -183,46 +203,51 @@ static int mld_parameters_cmp(void *ka, void *kb) {
return mldparm_link_compare(a, b);
}
-PICO_TREE_DECLARE(MLDParameters, mld_parameters_cmp);
+static PICO_TREE_DECLARE(MLDParameters, mcast_parameters_cmp);
-static int pico_mld_delete_parameter(struct mld_parameters *p) {
+static int pico_mld_delete_parameter(struct mcast_parameters *p)
+{
if (pico_tree_delete(&MLDParameters, p))
PICO_FREE(p);
else
return -1;
+
return 0;
}
-static void pico_mld_timer_expired(pico_time now, void *arg){
+static void pico_mld_timer_expired(pico_time now, void *arg)
+{
struct mld_timer *t = NULL, *timer = NULL, test = {
0
};
-#ifdef PICO_DEBUG_MLD
+#ifdef DEBUG_MLD
char ipstr[PICO_IPV6_STRING] = {
0
}, grpstr[PICO_IPV6_STRING] = {
0
};
-#endif
-
+#endif
+
IGNORE_PARAMETER(now);
t = (struct mld_timer *)arg;
test.type = t->type;
test.mcast_link = t->mcast_link;
test.mcast_group = t->mcast_group;
-#ifdef PICO_DEBUG_MLD
+#ifdef DEBUG_MLD
pico_ipv6_to_string(ipstr, t->mcast_link.addr);
pico_ipv6_to_string(grpstr, t->mcast_group.addr);
- mld_dbg("MLD: timer expired for %s link %s type %u, delay %llu\n", grpstr, ipstr, t->type, (uint64_t) t->delay);
+ mld_dbg("MLD: timer expired for %s link %s type %u, delay %lu\n", grpstr, ipstr, t->type, (uint64_t) t->delay);
#endif
timer = pico_tree_findKey(&MLDTimers, &test);
if (!timer) {
return;
}
+
if (timer->stopped == MLD_TIMER_STOPPED) {
pico_tree_delete(&MLDTimers, timer);
PICO_FREE(t);
return;
}
+
if (timer->start + timer->delay < PICO_TIME_MS()) {
pico_tree_delete(&MLDTimers, timer);
if (timer->mld_callback)
@@ -230,24 +255,30 @@ static void pico_mld_timer_expired(pico_time now, void *arg){
PICO_FREE(timer);
} else {
-#ifdef PICO_DEBUG_MLD
- mld_dbg("MLD: restart timer for %s, delay %llu, new delay %llu\n", grpstr, t->delay, (timer->start + timer->delay) - PICO_TIME_MS());
+#ifdef DEBUG_MLD
+ mld_dbg("MLD: restart timer for %s, delay %lu, new delay %lu\n", grpstr, t->delay, (timer->start + timer->delay) - PICO_TIME_MS());
#endif
- pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_mld_timer_expired, timer);
+ if (!pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_mld_timer_expired, timer)) {
+ mld_dbg("MLD: Failed to start expiration timer\n");
+ pico_tree_delete(&MLDTimers, timer);
+ PICO_FREE(timer);
+ }
}
+
return;
}
-static int pico_mld_timer_reset(struct mld_timer *t) {
+static int pico_mld_timer_reset(struct mld_timer *t)
+{
struct mld_timer *timer = NULL, test = {
0
};
-#ifdef PICO_DEBUG_MLD
+#ifdef DEBUG_MLD
char grpstr[PICO_IPV6_STRING] = {
0
};
pico_ipv6_to_string(grpstr, t->mcast_group.addr);
- mld_dbg("MLD: reset timer for %s, delay %llu\n", grpstr, t->delay);
+ mld_dbg("MLD: reset timer for %s, delay %lu\n", grpstr, t->delay);
#endif
test.type = t->type;
test.mcast_link = t->mcast_link;
@@ -261,11 +292,12 @@ static int pico_mld_timer_reset(struct mld_timer *t) {
return 0;
}
-static int pico_mld_timer_start(struct mld_timer *t) {
+static int pico_mld_timer_start(struct mld_timer *t)
+{
struct mld_timer *timer = NULL, test = {
0
};
-#ifdef PICO_DEBUG_MLD
+#ifdef DEBUG_MLD
char ipstr[PICO_IPV6_STRING] = {
0
}, grpstr[PICO_IPV6_STRING] = {
@@ -273,8 +305,8 @@ static int pico_mld_timer_start(struct mld_timer *t) {
};
pico_ipv6_to_string(ipstr, t->mcast_link.addr);
pico_ipv6_to_string(grpstr, t->mcast_group.addr);
- mld_dbg("MLD: start timer for %s link %s type %u, delay %llu\n", grpstr, ipstr, t->type, t->delay);
-#endif
+ mld_dbg("MLD: start timer for %s link %s type %u, delay %lu\n", grpstr, ipstr, t->type, t->delay);
+#endif
test.type = t->type;
test.mcast_link = t->mcast_link;
test.mcast_group = t->mcast_group;
@@ -287,18 +319,29 @@ static int pico_mld_timer_start(struct mld_timer *t) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
+
*timer = *t;
timer->start = PICO_TIME_MS();
- pico_tree_insert(&MLDTimers, timer);
- pico_timer_add(timer->delay, &pico_mld_timer_expired, timer);
+ if (pico_tree_insert(&MLDTimers, timer)) {
+ mld_dbg("MLD: Failed to insert timer into tree\n");
+ return -1;
+ }
+
+ if (!pico_timer_add(timer->delay, &pico_mld_timer_expired, timer)) {
+ mld_dbg("MLD: Failed to start expiration timer\n");
+ pico_tree_delete(&MLDTimers, timer);
+ PICO_FREE(timer);
+ return -1;
+ }
return 0;
}
-static int pico_mld_timer_stop(struct mld_timer *t) {
+static int pico_mld_timer_stop(struct mld_timer *t)
+{
struct mld_timer *timer = NULL, test = {
0
};
-#ifdef PICO_DEBUG_MLD
+#ifdef DEBUG_MLD
char grpstr[PICO_IPV6_STRING] = {
0
};
@@ -309,15 +352,17 @@ static int pico_mld_timer_stop(struct mld_timer *t) {
timer = pico_tree_findKey(&MLDTimers, &test);
if (!timer)
return -1;
-#ifdef PICO_DEBUG_MLD
+
+#ifdef DEBUG_MLD
pico_ipv6_to_string(grpstr, timer->mcast_group.addr);
- mld_dbg("MLD: stop timer for %s, delay %llu\n", grpstr, timer->delay);
-#endif
+ mld_dbg("MLD: stop timer for %s, delay %lu\n", grpstr, timer->delay);
+#endif
timer->stopped = MLD_TIMER_STOPPED;
return 0;
}
-static int pico_mld_timer_is_running(struct mld_timer *t) {
+static int pico_mld_timer_is_running(struct mld_timer *t)
+{
struct mld_timer *timer = NULL, test = {
0
};
@@ -332,7 +377,8 @@ static int pico_mld_timer_is_running(struct mld_timer *t) {
return 0;
}
-static struct mld_timer *pico_mld_find_timer(uint8_t type, struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group) {
+static struct mld_timer *pico_mld_find_timer(uint8_t type, struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group)
+{
struct mld_timer test = {
0
};
@@ -343,31 +389,37 @@ static struct mld_timer *pico_mld_find_timer(uint8_t type, struct pico_ip6 *mcas
return pico_tree_findKey(&MLDTimers, &test);
}
-static int mld_sources_cmp(void *ka, void *kb) {
+static int mld_sources_cmp(void *ka, void *kb)
+{
struct pico_ip6 *a = ka, *b = kb;
return pico_ipv6_compare(a, b);
}
-PICO_TREE_DECLARE(MLDAllow, mld_sources_cmp);
-PICO_TREE_DECLARE(MLDBlock, mld_sources_cmp);
+static PICO_TREE_DECLARE(MLDAllow, mld_sources_cmp);
+static PICO_TREE_DECLARE(MLDBlock, mld_sources_cmp);
-static struct mld_parameters *pico_mld_find_parameter(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group) {
- struct mld_parameters test = {
+static struct mcast_parameters *pico_mld_find_parameter(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group)
+{
+ struct mcast_parameters test = {
0
};
if (!mcast_link || !mcast_group)
return NULL;
- test.mcast_link = *mcast_link;
- test.mcast_group = *mcast_group;
+
+ test.mcast_link.ip6 = *mcast_link;
+ test.mcast_group.ip6 = *mcast_group;
return pico_tree_findKey(&MLDParameters, &test);
-}
-static int pico_mld_is_checksum_valid(struct pico_frame *f) {
+}
+static int pico_mld_is_checksum_valid(struct pico_frame *f)
+{
if( pico_icmp6_checksum(f) == 0)
return 1;
+
mld_dbg("ICMP6 (MLD) : invalid checksum\n");
return 0;
}
-uint16_t pico_mld_checksum(struct pico_frame *f) {
+uint16_t pico_mld_checksum(struct pico_frame *f)
+{
struct pico_ipv6_pseudo_hdr pseudo;
struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
struct mldv2_report *icmp6_hdr = (struct mldv2_report *)(f->transport_hdr + MLD_ROUTER_ALERT_LEN);
@@ -384,21 +436,30 @@ uint16_t pico_mld_checksum(struct pico_frame *f) {
return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv6_pseudo_hdr), icmp6_hdr, len);
}
/* RFC 3810 $8 */
-static int pico_mld_compatibility_mode(struct pico_frame *f) {
+static int pico_mld_compatibility_mode(struct pico_frame *f)
+{
struct pico_ipv6_link *link = NULL;
struct mld_timer t = {
0
};
- uint16_t datalen;
+ uint16_t datalen;
struct pico_tree_node *index = NULL, *_tmp = NULL;
+ struct pico_icmp6_hdr *hdr = (struct pico_icmp6_hdr *) (f->transport_hdr + MLD_ROUTER_ALERT_LEN);
+ struct mcast_parameters *p = NULL;
+ struct pico_ip6 mcast_group = {{
+ 0
+ }};
+ struct mld_message *mld_report = (struct mld_message *) hdr;
+
link = pico_ipv6_link_by_dev(f->dev);
if (!link)
return -1;
-
+
datalen = (uint16_t)(f->buffer_len - PICO_SIZE_IP6HDR - MLD_ROUTER_ALERT_LEN);
if (f->dev->eth) {
datalen = (uint16_t)(datalen - PICO_SIZE_ETHHDR);
}
+
if( datalen >= 28) {
/* MLDv2 */
t.type = MLD_TIMER_V2_QUERIER;
@@ -418,22 +479,34 @@ static int pico_mld_compatibility_mode(struct pico_frame *f) {
}
mld_dbg("MLD: switch to compatibility mode MLDv1\n");
link->mcast_compatibility = PICO_MLDV1;
+
+ /* Reset states to prevent deadlock */
+ mcast_group = mld_report->mcast_group;
+ p = pico_mld_find_parameter(&link->address, &mcast_group);
+ if(p) {
+ p->state = MLD_STATE_NON_LISTENER;
+ p->event = MLD_EVENT_START_LISTENING;
+ }
+
t.type = MLD_TIMER_V1_QUERIER;
- t.delay =(pico_time) ((MLD_ROBUSTNESS * link->mcast_last_query_interval) + MLD_QUERY_RESPONSE_INTERVAL) * 1000;
+ t.delay = (pico_time) ((MLD_ROBUSTNESS * link->mcast_last_query_interval) + MLD_QUERY_RESPONSE_INTERVAL) * 1000;
t.f = f;
t.mld_callback = pico_mld_v1querier_expired;
- pico_mld_timer_start(&t);
+ if (pico_mld_timer_start(&t) < 0)
+ return -1;
} else {
/* invalid query, silently ignored */
return -1;
}
+
return 0;
}
-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) {
- struct mld_parameters *p = NULL;
+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)
+{
+ struct mcast_parameters *p = NULL;
struct pico_ip6 ipv6;
-
+
pico_string_to_ipv6(MLD_ALL_HOST_GROUP, &ipv6.addr[0]);
if (!memcmp(&mcast_group->addr, &ipv6, sizeof(struct pico_ip6)))
@@ -441,7 +514,7 @@ int pico_mld_state_change(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_gr
p = pico_mld_find_parameter(mcast_link, mcast_group);
if (!p && state == PICO_MLD_STATE_CREATE) {
- p = PICO_ZALLOC(sizeof(struct mld_parameters));
+ p = PICO_ZALLOC(sizeof(struct mcast_parameters));
if (!p) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -449,13 +522,17 @@ int pico_mld_state_change(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_gr
if (!mcast_link || !mcast_group) {
pico_err = PICO_ERR_EINVAL;
+ PICO_FREE(p);
return -1;
}
p->state = MLD_STATE_NON_LISTENER;
- p->mcast_link = *mcast_link;
- p->mcast_group = *mcast_group;
- pico_tree_insert(&MLDParameters, p);
+ p->mcast_link.ip6 = *mcast_link;
+ p->mcast_group.ip6 = *mcast_group;
+ if(pico_tree_insert(&MLDParameters, p)){
+ PICO_FREE(p);
+ return -1;
+ }
} else if (!p) {
pico_err = PICO_ERR_EINVAL;
return -1;
@@ -481,48 +558,58 @@ int pico_mld_state_change(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_gr
return pico_mld_process_event(p);
}
/* finite state machine caller */
-static int pico_mld_process_event(struct mld_parameters *p);
+static int pico_mld_process_event(struct mcast_parameters *p);
-static struct mld_parameters *pico_mld_analyse_packet(struct pico_frame *f) {
- struct pico_icmp6_hdr *hdr = (struct pico_icmp6_hdr *) (f->transport_hdr+MLD_ROUTER_ALERT_LEN);
+static struct mcast_parameters *pico_mld_analyse_packet(struct pico_frame *f)
+{
+ struct pico_icmp6_hdr *hdr = (struct pico_icmp6_hdr *) (f->transport_hdr + MLD_ROUTER_ALERT_LEN);
struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *) f->net_hdr;
struct pico_ipv6_link *link = NULL;
- struct mld_parameters *p = NULL;
+ struct mcast_parameters *p = NULL;
struct pico_ip6 mcast_group = {{
- 0
- }};
+ 0
+ }};
struct mld_message *mld_report = (struct mld_message *) hdr;
struct pico_ipv6_exthdr *hbh;
-
+
link = pico_ipv6_link_by_dev(f->dev);
- if(!link)
+ if(!link)
return NULL;
+
mcast_group = mld_report->mcast_group;
/* Package check */
if(ipv6_hdr->hop != MLD_HOP_LIMIT) {
mld_dbg("MLD: Hop limit > 1, ignoring frame\n");
return NULL;
}
+
hbh = (struct pico_ipv6_exthdr *) (f->transport_hdr);
if(pico_mld_check_hopbyhop((struct pico_ipv6_hbhoption *)hbh) < 0) {
mld_dbg("MLD: Router Alert option is not set\n");
return NULL;
}
- if(!pico_ipv6_is_linklocal(ipv6_hdr->src.addr) || pico_ipv6_is_unspecified(ipv6_hdr->src.addr) ) {
+
+ if(!pico_ipv6_is_linklocal(ipv6_hdr->src.addr) || pico_ipv6_is_unspecified(ipv6_hdr->src.addr)) {
mld_dbg("MLD Source is invalid link-local address\n");
return NULL;
}
+
/* end package check */
- p = pico_mld_find_parameter(&link->address, &mcast_group);
+ p = pico_mld_find_parameter(&link->address, &mcast_group);
if(!p) {
mld_dbg("Alloc-ing MLD parameters\n");
- p = PICO_ZALLOC(sizeof(struct mld_parameters));
+ p = PICO_ZALLOC(sizeof(struct mcast_parameters));
if(!p)
return NULL;
+
p->state = MLD_STATE_NON_LISTENER;
- p->mcast_link = link->address;
- pico_tree_insert(&MLDParameters,p);
- }
+ p->mcast_link.ip6 = link->address;
+ if (pico_tree_insert(&MLDParameters, p)) {
+ PICO_FREE(p);
+ return NULL;
+ }
+ }
+
mld_dbg("Analyse package, type = %d\n", hdr->type);
switch(hdr->type) {
case PICO_MLD_QUERY:
@@ -541,21 +628,22 @@ static struct mld_parameters *pico_mld_analyse_packet(struct pico_frame *f) {
default:
return NULL;
}
- p->f = f;
+ p->f = f;
return p;
}
-int pico_mld_process_in(struct pico_frame *f) {
- struct mld_parameters *p = NULL;
-
- if (!pico_mld_is_checksum_valid(f))
- goto out;
-
- if (pico_mld_compatibility_mode(f) < 0)
+int pico_mld_process_in(struct pico_frame *f)
+{
+ struct mcast_parameters *p = NULL;
+
+ if (!pico_mld_is_checksum_valid(f))
goto out;
-
+
+ if (pico_mld_compatibility_mode(f) < 0)
+ goto out;
+
if((p = pico_mld_analyse_packet(f)) == NULL)
goto out;
-
+
return pico_mld_process_event(p);
out:
mld_dbg("FRAME DISCARD\n");
@@ -565,72 +653,76 @@ out:
-static int8_t pico_mld_send_done(struct mld_parameters *p, struct pico_frame *f) {
+static int8_t pico_mld_send_done(struct mcast_parameters *p, struct pico_frame *f)
+{
struct mld_message *report = NULL;
uint8_t report_type = PICO_MLD_DONE;
+ struct pico_device *dev = NULL;
struct pico_ipv6_exthdr *hbh;
- struct pico_ip6 dst = {{
- 0
- }};
-#ifdef PICO_DEBUG_MLD
+ struct pico_ip6 dst = {{ 0 }};
+#ifdef DEBUG_MLD
char ipstr[PICO_IPV6_STRING] = {
0
- }, grpstr[PICO_IPV6_STRING] ={
+ }, grpstr[PICO_IPV6_STRING] = {
0
};
#endif
IGNORE_PARAMETER(f);
pico_string_to_ipv6(MLD_ALL_ROUTER_GROUP, &dst.addr[0]);
- p->f = pico_proto_ipv6.alloc(&pico_proto_ipv6, sizeof(struct mld_message)+MLD_ROUTER_ALERT_LEN);
- p->f->dev = pico_ipv6_link_find(&p->mcast_link);
+ dev = pico_ipv6_link_find(&p->mcast_link.ip6);
+ p->f = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, sizeof(struct mld_message) + MLD_ROUTER_ALERT_LEN);
/* p->f->len is correctly set by alloc */
hbh = (struct pico_ipv6_exthdr *)(p->f->transport_hdr);
report = (struct mld_message *)(pico_mld_fill_hopbyhop((struct pico_ipv6_hbhoption*)hbh));
if(!report) {
- pico_err = PICO_ERR_ENOMEM;
+ pico_err = PICO_ERR_ENOMEM;
return -1;
}
+
report->type = report_type;
- report->max_resp_delay = 0;
- report->mcast_group = p->mcast_group;
+ report->max_resp_delay = 0;
+ report->mcast_group = p->mcast_group.ip6;
report->crc = 0;
- //Checksum done in ipv6 module, no need to do it twice
- //report->crc = short_be(pico_icmp6_checksum(p->f));
-#ifdef PICO_DEBUG_MLD
+ /* Checksum done in ipv6 module, no need to do it twice */
+ /* report->crc = short_be(pico_icmp6_checksum(p->f)); */
+#ifdef DEBUG_MLD
pico_ipv6_to_string(ipstr, dst.addr);
- pico_ipv6_to_string(grpstr, mcast_group.addr);
+ pico_ipv6_to_string(grpstr, report->mcast_group.addr);
mld_dbg("MLD: send membership done on group %s to %s\n", grpstr, ipstr);
-#endif
- pico_ipv6_frame_push(p->f, NULL, &dst, 0,0);
+#endif
+ pico_ipv6_frame_push(p->f, NULL, &dst, 0, 0);
return 0;
}
-static int pico_mld_send_report(struct mld_parameters *p, struct pico_frame *f) {
+
+static int pico_mld_send_report(struct mcast_parameters *p, struct pico_frame *f)
+{
struct pico_ip6 dst = {{
- 0
- }};
+ 0
+ }};
struct pico_ip6 mcast_group = {{
- 0
- }};
-#ifdef PICO_DEBUG_MLD
+ 0
+ }};
+#ifdef DEBUG_MLD
char ipstr[PICO_IPV6_STRING] = {
0
- }, grpstr[PICO_IPV6_STRING] ={
+ }, grpstr[PICO_IPV6_STRING] = {
0
};
#endif
struct pico_ipv6_link *link = NULL;
- link = pico_ipv6_link_get(&p->mcast_link);
+ link = pico_ipv6_link_get(&p->mcast_link.ip6);
if (!link)
return -1;
- mcast_group = p->mcast_group;
+ mcast_group = p->mcast_group.ip6;
switch (link->mcast_compatibility) {
case PICO_MLDV1:
if (p->event == MLD_EVENT_STOP_LISTENING)
pico_string_to_ipv6(MLD_ALL_ROUTER_GROUP, &dst.addr[0]);
- else
+ else
dst = mcast_group;
+
break;
case PICO_MLDV2:
pico_string_to_ipv6(MLDV2_ALL_ROUTER_GROUP, &dst.addr[0]);
@@ -639,269 +731,134 @@ static int pico_mld_send_report(struct mld_parameters *p, struct pico_frame *f)
pico_err = PICO_ERR_EPROTONOSUPPORT;
return -1;
}
-#ifdef PICO_DEBUG_MLD
+#ifdef DEBUG_MLD
pico_ipv6_to_string(ipstr, dst.addr);
pico_ipv6_to_string(grpstr, mcast_group.addr);
mld_dbg("MLD: send membership report on group %s to %s\n", grpstr, ipstr);
#endif
- pico_ipv6_frame_push(f, NULL, &dst, 0,0);
+ pico_ipv6_frame_push(f, NULL, &dst, 0, 0);
return 0;
}
+static int8_t pico_mldv2_generate_report(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
+{
+ struct mldv2_report *report = NULL;
+ struct mldv2_group_record *record = NULL;
+ struct pico_tree_node *index = NULL;
+ struct pico_ipv6_hbhoption *hbh;
+ struct pico_device *dev = NULL;
+ uint16_t len = 0;
+ uint16_t i = 0;
+ /* RFC3810 $5.1.10 */
+ if(filter->sources > MLD_MAX_SOURCES) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
-static int8_t pico_mld_generate_report(struct mld_parameters *p) {
- struct pico_ipv6_link *link = NULL;
- uint8_t i = 0;
- link = pico_ipv6_link_get(&p->mcast_link);
- if (!link) {
+ len = (uint16_t)(sizeof(struct mldv2_report) + sizeof(struct mldv2_group_record) \
+ + (filter->sources * sizeof(struct pico_ip6)) + MLD_ROUTER_ALERT_LEN);
+ len = (uint16_t)(len - sizeof(struct pico_ip6));
+ dev = pico_ipv6_link_find(&p->mcast_link.ip6);
+ p->f = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
+ /* p->f->len is correctly set by alloc */
+
+ hbh = (struct pico_ipv6_hbhoption *) p->f->transport_hdr;
+ report = (struct mldv2_report *)(pico_mld_fill_hopbyhop(hbh));
+ report->type = PICO_MLD_REPORTV2;
+ report->res = 0;
+ report->crc = 0;
+ report->res1 = 0;
+ report->nbr_gr = short_be(1);
+
+ record = &report->record[0];
+ record->type = filter->record_type;
+ record->aux = 0;
+ record->nbr_src = short_be(filter->sources);
+ record->mcast_group = p->mcast_group.ip6;
+ if (filter->filter && !pico_tree_empty(filter->filter)) {
+ i = 0;
+ pico_tree_foreach(index, filter->filter)
+ {
+ record->src[i] = (*(struct pico_ip6 *)index->keyValue);
+ i++;
+ }
+ }
+
+ if(i != filter->sources) {
+ return -1;
+ }
+
+ /* Checksum done in ipv6 module, no need to do it twice */
+ /* report->crc= short_be(pico_mld_checksum(p->f)); */
+ return 0;
+}
+static int8_t pico_mldv2_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
+{
+ struct pico_mcast_group *g = NULL, test = {
+ 0
+ };
+ struct pico_tree *MLDFilter = NULL;
+ struct pico_ipv6_link *link = (struct pico_ipv6_link*) filter->link;
+ filter->p = (struct mcast_parameters *)p;
+ filter->allow = &MLDAllow;
+ filter->block = &MLDBlock;
+ filter->filter = MLDFilter;
+ filter->sources = 0;
+ filter->proto = PICO_MLDV2;
+ test.mcast_addr = p->mcast_group;
+ g = pico_tree_findKey(link->MCASTGroups, &test);
+ if (!g) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
- if( !pico_ipv6_is_multicast(p->mcast_group.addr) ) {
+
+ filter->g = (struct pico_mcast_group *)g;
+ return pico_mcast_generate_filter(filter, p);
+
+}
+static int8_t pico_mldv1_generate_report(struct mcast_parameters *p)
+{
+ struct mld_message *report = NULL;
+ uint8_t report_type = PICO_MLD_REPORT;
+ struct pico_ipv6_exthdr *hbh;
+ struct pico_device *dev = pico_ipv6_link_find(&p->mcast_link.ip6);
+ p->f = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, sizeof(struct mld_message) + MLD_ROUTER_ALERT_LEN );
+ /* p->f->len is correctly set by alloc */
+
+ hbh = (struct pico_ipv6_exthdr *)(p->f->transport_hdr);
+ report = (struct mld_message *)(pico_mld_fill_hopbyhop((struct pico_ipv6_hbhoption *)hbh));
+ report->type = report_type;
+ report->max_resp_delay = MLD_DEFAULT_MAX_RESPONSE_TIME;
+ report->mcast_group = p->mcast_group.ip6;
+
+ report->crc = 0;
+ /* Checksum done in ipv6 module, no need to do it twice */
+ /* report->crc = short_be(pico_icmp6_checksum(p->f)); */
+ return 0;
+}
+static int8_t pico_mld_generate_report(struct mcast_parameters *p)
+{
+ struct mcast_filter_parameters filter;
+ int8_t result;
+ filter.link = (union pico_link *)pico_ipv6_link_get(&p->mcast_link.ip6);
+ if( !filter.link || !pico_ipv6_is_multicast(p->mcast_group.ip6.addr)) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
- switch (link->mcast_compatibility) {
+
+ switch (filter.link->ipv6.mcast_compatibility) {
case PICO_MLDV1: {
- struct mld_message *report = NULL;
- uint8_t report_type = PICO_MLD_REPORT;
- struct pico_ipv6_exthdr *hbh;
- p->f = pico_proto_ipv6.alloc(&pico_proto_ipv6, sizeof(struct mld_message)+MLD_ROUTER_ALERT_LEN );
- p->f->dev = pico_ipv6_link_find(&p->mcast_link);
- /* p->f->len is correctly set by alloc */
-
- hbh = (struct pico_ipv6_exthdr *)(p->f->transport_hdr);
- report = (struct mld_message *)(pico_mld_fill_hopbyhop((struct pico_ipv6_hbhoption *)hbh));
- report->type = report_type;
- report->max_resp_delay = MLD_DEFAULT_MAX_RESPONSE_TIME;
- report->mcast_group = p->mcast_group;
-
- report->crc = 0;
- //Checksum done in ipv6 module, no need to do it twice
- //report->crc = short_be(pico_icmp6_checksum(p->f));
- break;
+ return pico_mldv1_generate_report(p);
}
case PICO_MLDV2: {
- struct mldv2_report *report = NULL;
- struct mldv2_group_record *record = NULL;
- struct pico_ipv6_mcast_group *g = NULL, test = {
- 0
- };
- struct pico_tree_node *index = NULL, *_tmp = NULL;
- struct pico_tree *MLDFilter = NULL;
- struct pico_ip6 *source = NULL;
- struct pico_ipv6_hbhoption *hbh;
- uint8_t record_type = 0;
- uint8_t sources = 0;
- uint16_t len = 0;
- test.mcast_addr = p->mcast_group;
- g = pico_tree_findKey(link->MCASTGroups, &test);
- if (!g) {
- pico_err = PICO_ERR_EINVAL;
+ result = pico_mldv2_generate_filter(&filter, p);
+ if(result < 0)
return -1;
- }
- /* "non-existent" state of filter mode INCLUDE and empty source list */
- if (p->event == MLD_EVENT_DELETE_GROUP) {
- p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
- p->MCASTFilter = NULL;
- }
- if (p->event == MLD_EVENT_QUERY_RECV)
- goto mld2_report;
- /* cleanup filters */
- pico_tree_foreach_safe(index, &MLDAllow, _tmp)
- {
- pico_tree_delete(&MLDAllow, index->keyValue);
- }
- pico_tree_foreach_safe(index, &MLDBlock, _tmp)
- {
- pico_tree_delete(&MLDBlock, index->keyValue);
- }
- switch (g->filter_mode) {
- case PICO_IP_MULTICAST_INCLUDE:
- switch (p->filter_mode) {
- case PICO_IP_MULTICAST_INCLUDE:
- /* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
- if (p->event == MLD_EVENT_DELETE_GROUP) {
- /* TO_IN (B) */
- record_type = MLD_CHANGE_TO_INCLUDE_MODE;
- MLDFilter = &MLDAllow;
- if (p->MCASTFilter) {
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&MLDAllow, index->keyValue);
- sources++;
- }
- } /* else { MLDAllow stays empty } */
-
- break;
- }
-
- /* ALLOW (B-A) */
- /* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
- if (p->event == MLD_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
- record_type = MLD_CHANGE_TO_INCLUDE_MODE;
- else
- record_type = MLD_ALLOW_NEW_SOURCES;
-
- MLDFilter = &MLDAllow;
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&MLDAllow, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, &g->MCASTSources) /* A */
- {
- source = pico_tree_findKey(&MLDAllow, index->keyValue);
- if (source) {
- pico_tree_delete(&MLDAllow, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&MLDAllow)) /* record type is ALLOW */
- break;
-
- /* BLOCK (A-B) */
- record_type = MLD_BLOCK_OLD_SOURCES;
- MLDFilter = &MLDBlock;
- pico_tree_foreach(index, &g->MCASTSources) /* A */
- {
- pico_tree_insert(&MLDBlock, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- source = pico_tree_findKey(&MLDBlock, index->keyValue);
- if (source) {
- pico_tree_delete(&MLDBlock, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&MLDBlock)) /* record type is BLOCK */
- break;
-
- /* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report */
- p->f = NULL;
- return 0;
- case PICO_IP_MULTICAST_EXCLUDE:
- /* TO_EX (B) */
- record_type = MLD_CHANGE_TO_EXCLUDE_MODE;
- MLDFilter = &MLDBlock;
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&MLDBlock, index->keyValue);
- sources++;
- }
- break;
- default:
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
- break;
- case PICO_IP_MULTICAST_EXCLUDE:
- switch (p->filter_mode) {
- case PICO_IP_MULTICAST_INCLUDE:
- /* TO_IN (B) */
- record_type = MLD_CHANGE_TO_INCLUDE_MODE;
- MLDFilter = &MLDAllow;
- if (p->MCASTFilter) {
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- pico_tree_insert(&MLDAllow, index->keyValue);
- sources++;
- }
- } /* else { MLDAllow stays empty } */
-
- break;
- case PICO_IP_MULTICAST_EXCLUDE:
- /* BLOCK (B-A) */
- record_type = MLD_BLOCK_OLD_SOURCES;
- MLDFilter = &MLDBlock;
- pico_tree_foreach(index, p->MCASTFilter)
- {
- pico_tree_insert(&MLDBlock, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, &g->MCASTSources) /* A */
- {
- source = pico_tree_findKey(&MLDBlock, index->keyValue); /* B */
- if (source) {
- pico_tree_delete(&MLDBlock, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&MLDBlock)) /* record type is BLOCK */
- break;
- /* ALLOW (A-B) */
- record_type = MLD_ALLOW_NEW_SOURCES;
- MLDFilter = &MLDAllow;
- pico_tree_foreach(index, &g->MCASTSources)
- {
- pico_tree_insert(&MLDAllow, index->keyValue);
- sources++;
- }
- pico_tree_foreach(index, p->MCASTFilter) /* B */
- {
- source = pico_tree_findKey(&MLDAllow, index->keyValue); /* A */
- if (source) {
- pico_tree_delete(&MLDAllow, source);
- sources--;
- }
- }
- if (!pico_tree_empty(&MLDAllow)) /* record type is ALLOW */
- break;
- /* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report */
- p->f = NULL;
- return 0;
- default:
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
- break;
- default:
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
-mld2_report:
- /* RFC3810 $5.1.10 */
- if(sources > MLD_MAX_SOURCES) {
- pico_err = PICO_ERR_EINVAL;
- return -1;
- }
- len = (uint16_t)(sizeof(struct mldv2_report) + sizeof(struct mldv2_group_record) \
- + (sources * sizeof(struct pico_ip6))+MLD_ROUTER_ALERT_LEN);
-
- p->f = pico_proto_ipv6.alloc(&pico_proto_ipv6, len);
- p->f->dev = pico_ipv6_link_find(&p->mcast_link);
- /* p->f->len is correctly set by alloc */
-
- hbh = (struct pico_ipv6_hbhoption *) p->f->transport_hdr;
- report = (struct mldv2_report *)(pico_mld_fill_hopbyhop(hbh));
- report->type = PICO_MLD_REPORTV2;
- report->res = 0;
- report->crc = 0;
- report->res1 = 0;
- report->nbr_gr = short_be(1);
-
- record = &report->record[0];
- record->type = record_type;
- record->aux = 0;
- record->nbr_src = short_be(sources);
- record->mcast_group = p->mcast_group;
- if (MLDFilter && !pico_tree_empty(MLDFilter)) {
- i = 0;
- pico_tree_foreach(index, MLDFilter)
- {
- record->src[i] = (*(struct pico_ip6 *)index->keyValue);
- i++;
- }
- }
- if(i != sources)
- return -1;
- //Checksum done in ipv6 module, no need to do it twice
- //report->crc= short_be(pico_mld_checksum(p->f));
- break;
- }
+ if(result != MCAST_NO_REPORT)
+ return pico_mldv2_generate_report(&filter, p);
+ }
+ break;
default:
pico_err = PICO_ERR_EINVAL;
return -1;
@@ -910,42 +867,48 @@ mld2_report:
return 0;
}
/* stop timer, send done if flag set */
-static int mld_stsdifs(struct mld_parameters *p) {
+static int mld_stsdifs(struct mcast_parameters *p)
+{
struct mld_timer t = {
0
};
struct pico_ipv6_link *link = NULL;
struct pico_frame *copy_frame = NULL;
- link = pico_ipv6_link_get(&p->mcast_link);
+ link = pico_ipv6_link_get(&p->mcast_link.ip6);
if (!link)
return -1;
mld_dbg("MLD: event = stop listening | action = stop timer, send done if flag set\n");
t.type = MLD_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip6;
+ t.mcast_group = p->mcast_group.ip6;
if (pico_mld_timer_stop(&t) < 0)
return -1;
- switch(link->mcast_compatibility){
- case PICO_MLDV2:
- if (pico_mld_generate_report(p) < 0) {
- return -1;
- }
- copy_frame = pico_frame_copy(p->f);
- if (!copy_frame) {
- pico_err = PICO_ERR_ENOMEM;
- return -1;
- }
- if (pico_mld_send_report(p, copy_frame) < 0) {
- return -1;
- }
- break;
- case PICO_MLDV1:
- /* Send done if flag is set */
- if (pico_mld_flag && pico_mld_send_done(p, p->f) < 0)
- return -1;
- break;
+
+ switch(link->mcast_compatibility) {
+ case PICO_MLDV2:
+ if (pico_mld_generate_report(p) < 0) {
+ return -1;
+ }
+
+ copy_frame = pico_frame_copy(p->f);
+ if (!copy_frame) {
+ pico_err = PICO_ERR_ENOMEM;
+ return -1;
+ }
+
+ if (pico_mld_send_report(p, copy_frame) < 0) {
+ return -1;
+ }
+
+ break;
+ case PICO_MLDV1:
+ /* Send done if flag is set */
+ if (pico_mld_flag && pico_mld_send_done(p, p->f) < 0)
+ return -1;
+
+ break;
}
pico_mld_delete_parameter(p);
@@ -953,7 +916,8 @@ static int mld_stsdifs(struct mld_parameters *p) {
return 0;
}
/* send report, set flag, start timer */
-static int mld_srsfst(struct mld_parameters *p) {
+static int mld_srsfst(struct mcast_parameters *p)
+{
struct mld_timer t = {
0
};
@@ -961,27 +925,32 @@ static int mld_srsfst(struct mld_parameters *p) {
mld_dbg("MLD: event = start listening | action = send report, set flag, start timer\n");
p->last_host = MLD_HOST_LAST;
- if (pico_mld_generate_report(p) < 0)
- return -1;
-
+ if (pico_mld_generate_report(p) < 0)
+ return -1;
+
if (!p->f)
return 0;
+
copy_frame = pico_frame_copy(p->f);
if (!copy_frame) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
+
if (pico_mld_send_report(p, copy_frame) < 0)
return -1;
t.type = MLD_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
-
+ t.mcast_link = p->mcast_link.ip6;
+ t.mcast_group = p->mcast_group.ip6;
+
t.delay = (pico_rand() % (MLD_UNSOLICITED_REPORT_INTERVAL * 10000));
t.f = p->f;
t.mld_callback = pico_mld_report_expired;
- pico_mld_timer_start(&t);
+
+ if (pico_mld_timer_start(&t) < 0)
+ return -1;
+
pico_mld_flag = 1;
p->state = MLD_STATE_DELAYING_LISTENER;
mld_dbg("MLD: new state = Delaying Listener\n");
@@ -989,7 +958,8 @@ static int mld_srsfst(struct mld_parameters *p) {
}
/* stop timer, clear flag */
-static int mld_stcl(struct mld_parameters *p) {
+static int mld_stcl(struct mcast_parameters *p)
+{
struct mld_timer t = {
0
};
@@ -997,10 +967,11 @@ static int mld_stcl(struct mld_parameters *p) {
mld_dbg("MLD: event = report received | action = stop timer, clear flag\n");
t.type = MLD_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip6;
+ t.mcast_group = p->mcast_group.ip6;
if (pico_mld_timer_stop(&t) < 0)
return -1;
+
pico_mld_flag = 0;
p->last_host = MLD_HOST_NOT_LAST;
p->state = MLD_STATE_IDLE_LISTENER;
@@ -1008,24 +979,27 @@ static int mld_stcl(struct mld_parameters *p) {
return 0;
}
/* send report, set flag */
-static int mld_srsf(struct mld_parameters *p) {
+static int mld_srsf(struct mcast_parameters *p)
+{
mld_dbg("MLD: event = timer expired | action = send report, set flag\n");
if (pico_mld_send_report(p, p->f) < 0)
return -1;
+
pico_mld_flag = 1;
p->state = MLD_STATE_IDLE_LISTENER;
mld_dbg("MLD: new state = Idle Listener\n");
return 0;
}
/* reset timer if max response time < current timer */
-static int mld_rtimrtct(struct mld_parameters *p) {
+static int mld_rtimrtct(struct mcast_parameters *p)
+{
struct mld_timer *t = NULL;
uint32_t current_timer = 0;
mld_dbg("MLD: event = query received | action = reset timer if max response time < current timer\n");
- t = pico_mld_find_timer(MLD_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
+ t = pico_mld_find_timer(MLD_TIMER_GROUP_REPORT, &p->mcast_link.ip6, &p->mcast_group.ip6);
if (!t)
return -1;
@@ -1034,18 +1008,20 @@ static int mld_rtimrtct(struct mld_parameters *p) {
t->delay = pico_rand() % ((1u + p->max_resp_time) * 100u);
pico_mld_timer_reset(t);
}
+
p->state = MLD_STATE_DELAYING_LISTENER;
mld_dbg("MLD: new state = Delaying Listener\n");
return 0;
}
/* merge report, send report, reset timer (MLDv2 only) */
-static int mld_mrsrrt(struct mld_parameters *p) {
+static int mld_mrsrrt(struct mcast_parameters *p)
+{
struct mld_timer *t = NULL;
struct pico_frame *copy_frame = NULL;
struct pico_ipv6_link *link = NULL;
mld_dbg("MLD: event = update group | action = merge report, send report, reset timer (MLDv2 only)\n");
- link = pico_ipv6_link_get(&p->mcast_link);
+ link = pico_ipv6_link_get(&p->mcast_link.ip6);
if (!link)
return -1;
@@ -1059,11 +1035,11 @@ static int mld_mrsrrt(struct mld_parameters *p) {
copy_frame = pico_frame_copy(p->f);
if (!copy_frame)
return -1;
-
+
if (pico_mld_send_report(p, copy_frame) < 0)
return -1;
- t = pico_mld_find_timer(MLD_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
+ t = pico_mld_find_timer(MLD_TIMER_GROUP_REPORT, &p->mcast_link.ip6, &p->mcast_group.ip6);
if (!t)
return -1;
@@ -1076,7 +1052,8 @@ static int mld_mrsrrt(struct mld_parameters *p) {
}
/* send report, start timer (MLDv2 only) */
-static int mld_srst(struct mld_parameters *p){
+static int mld_srst(struct mcast_parameters *p)
+{
struct mld_timer t = {
0
};
@@ -1085,7 +1062,7 @@ static int mld_srst(struct mld_parameters *p){
mld_dbg("MLD: event = update group | action = send report, start timer (MLDv2 only)\n");
- link = pico_ipv6_link_get(&p->mcast_link);
+ link = pico_ipv6_link_get(&p->mcast_link.ip6);
if (!link)
return -1;
@@ -1108,38 +1085,43 @@ static int mld_srst(struct mld_parameters *p){
return -1;
t.type = MLD_TIMER_GROUP_REPORT;
- t.mcast_link = p->mcast_link;
- t.mcast_group = p->mcast_group;
+ t.mcast_link = p->mcast_link.ip6;
+ t.mcast_group = p->mcast_group.ip6;
t.delay = (pico_rand() % (MLD_UNSOLICITED_REPORT_INTERVAL * 10000));
t.f = p->f;
t.mld_callback = pico_mld_report_expired;
- pico_mld_timer_start(&t);
+
+ if (pico_mld_timer_start(&t) < 0)
+ return -1;
p->state = MLD_STATE_DELAYING_LISTENER;
mld_dbg("MLD: new state = delaying member\n");
return 0;
}
-static int mld_discard(struct mld_parameters *p) {
+static int mld_discard(struct mcast_parameters *p)
+{
mld_dbg("MLD: ignore and mld_discard frame\n");
- // the frame will be discared bij the ipv6 module!!!
+ /* the frame will be discared bij the ipv6 module!!! */
IGNORE_PARAMETER(p);
return 0;
}
-
+
/* finite state machine table */
static const mld_callback mld_state_diagram[3][6] =
{ /* event | Stop Listening | Start Listening | Update Group |Query reveive |Report receive |Timer expired */
-/* none listener*/ { mld_discard , mld_srsfst, mld_srsfst, mld_discard, mld_discard, mld_discard},
-/* idle listener */ { mld_stsdifs , mld_mrsrrt, mld_mrsrrt, mld_rtimrtct, mld_stcl, mld_srsf },
-/* delaying listener */ { mld_rtimrtct, mld_srst, mld_srst, mld_srsf, mld_stsdifs, mld_discard }
+/* none listener*/
+ { mld_discard, mld_srsfst, mld_srsfst, mld_discard, mld_discard, mld_discard},
+/* idle listener */ { mld_stsdifs, mld_mrsrrt, mld_mrsrrt, mld_rtimrtct, mld_stcl, mld_srsf },
+/* delaying listener */ { mld_rtimrtct, mld_srst, mld_srst, mld_srsf, mld_stsdifs, mld_discard }
};
-static int pico_mld_process_event(struct mld_parameters *p) {
- struct pico_tree_node *index= NULL;
- struct mld_parameters *_p;
-#ifdef PICO_DEBUG_MLD
+static int pico_mld_process_event(struct mcast_parameters *p)
+{
+ struct pico_tree_node *index = NULL;
+ struct mcast_parameters *_p;
+#ifdef DEBUG_MLD
char ipv6[PICO_IPV6_STRING];
- pico_ipv6_to_string(ipv6, p->mcast_group.addr);
+ pico_ipv6_to_string(ipv6, p->mcast_group.ip6.addr);
mld_dbg("MLD: process event on group address %s\n", ipv6);
#endif
if (p->event == MLD_EVENT_QUERY_RECV && p->general_query) { /* general query */
@@ -1147,28 +1129,32 @@ static int pico_mld_process_event(struct mld_parameters *p) {
_p = index->keyValue;
_p->max_resp_time = p->max_resp_time;
_p->event = MLD_EVENT_QUERY_RECV;
-#ifdef PICO_DEBUG_MLD
+#ifdef DEBUG_MLD
mld_dbg("MLD: for each mcast_group = %s | state = %u\n", ipv6, _p->state);
#endif
return mld_state_diagram[_p->state][_p->event](_p);
}
} else {
mld_dbg("MLD: state = %u (0: non-listener - 1: delaying listener - 2: idle listener) event = %u\n", p->state, p->event);
- return mld_state_diagram[p->state][p->event](p);
+ return mld_state_diagram[p->state][p->event](p);
}
+
return 0;
}
#else
-uint16_t pico_mld_checksum(struct pico_frame *f) {
+uint16_t pico_mld_checksum(struct pico_frame *f)
+{
IGNORE_PARAMETER(f);
return 0;
}
-int pico_mld_process_in(struct pico_frame *f) {
+int pico_mld_process_in(struct pico_frame *f)
+{
IGNORE_PARAMETER(f);
return -1;
}
-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) {
+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)
+{
IGNORE_PARAMETER(mcast_link);
IGNORE_PARAMETER(mcast_group);
IGNORE_PARAMETER(filter_mode);
diff --git a/ext/picotcp/modules/pico_mld.h b/ext/picotcp/modules/pico_mld.h
index 219ef4a..4318162 100644
--- a/ext/picotcp/modules/pico_mld.h
+++ b/ext/picotcp/modules/pico_mld.h
@@ -1,6 +1,6 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
- See LICENSE and COPYING for usage.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -86,11 +86,11 @@ struct mld_multicast_address_record {
uint8_t aux_len;
uint16_t nbr_src;
struct pico_ip6 multicast;
- struct pico_ip6 src[0];
+ struct pico_ip6 src[1];
};
struct mld_parameters {
- uint8_t event;
+ uint8_t event;
uint8_t state;
uint8_t general_query;
uint8_t filter_mode;
diff --git a/ext/picotcp/modules/pico_mm.c b/ext/picotcp/modules/pico_mm.c
index 5a6517b..f2553c1 100644
--- a/ext/picotcp/modules/pico_mm.c
+++ b/ext/picotcp/modules/pico_mm.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Gustav Janssens, Jonas Van Nieuwenberg, Sam Van Den Berge
*********************************************************************/
@@ -11,11 +11,19 @@
#include "pico_config.h"
#include "pico_protocol.h" /* For pico_err */
-#define DBG_MM(x, args ...) /* dbg("[%s:%s:%i] "x" \n",__FILE__,__func__,__LINE__ ,##args ) */
-#define DBG_MM_RED(x, args ...) /* dbg("\033[31m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args ) */
-#define DBG_MM_GREEN(x, args ...) /* dbg("\033[32m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args ) */
-#define DBG_MM_YELLOW(x, args ...) /* dbg("\033[33m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args ) */
-#define DBG_MM_BLUE(x, args ...) /* dbg("\033[34m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args ) */
+#ifdef DEBUG_MM
+#define DBG_MM(x, args ...) dbg("[%s:%s:%i] "x" \n",__FILE__,__func__,__LINE__ ,##args )
+#define DBG_MM_RED(x, args ...) dbg("\033[31m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
+#define DBG_MM_GREEN(x, args ...) dbg("\033[32m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
+#define DBG_MM_YELLOW(x, args ...) dbg("\033[33m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
+#define DBG_MM_BLUE(x, args ...) dbg("\033[34m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
+#else
+#define DBG_MM(x, args ...) do {} while(0)
+#define DBG_MM_RED(x, args ...) do {} while(0)
+#define DBG_MM_GREEN(x, args ...) do {} while(0)
+#define DBG_MM_YELLOW(x, args ...) do {} while(0)
+#define DBG_MM_BLUE(x, args ...) do {} while(0)
+#endif
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
* These nodes should be placed in the manager page which is in a different memory region then the nodes
@@ -973,8 +981,9 @@ void pico_mem_free(void*ptr)
{
struct pico_mem_block*generic_block;
struct pico_mem_page*page;
- /*Uncomment i for debugging!*/
- /*uint16_t i = 0;*/
+#ifdef DEBUG_MM
+ uint16_t i = 0;
+#endif
DBG_MM_YELLOW("Free called on %p", ptr);
@@ -1229,8 +1238,9 @@ void pico_mem_page0_free(void*ptr)
{
struct pico_mem_block*node = ptr;
struct pico_mem_manager_extra*heap_page;
- /* Uncomment for debugging! */
- /* int i = 0; */
+#ifdef DEBUG_MM
+ uint16_t i = 0;
+#endif
/* TODO: should be able to merge free neighbouring blocks (??) */
DBG_MM_YELLOW("page0_free called");
diff --git a/ext/picotcp/modules/pico_mm.h b/ext/picotcp/modules/pico_mm.h
index 29366d0..93bd102 100644
--- a/ext/picotcp/modules/pico_mm.h
+++ b/ext/picotcp/modules/pico_mm.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Gustav Janssens, Jonas Van Nieuwenberg, Sam Van Den Berge
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_nat.c b/ext/picotcp/modules/pico_nat.c
index 19f85e0..b8f08e0 100644
--- a/ext/picotcp/modules/pico_nat.c
+++ b/ext/picotcp/modules/pico_nat.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -19,8 +19,12 @@
#ifdef PICO_SUPPORT_IPV4
#ifdef PICO_SUPPORT_NAT
+#ifdef DEBUG_NAT
+#define nat_dbg dbg
+#else
#define nat_dbg(...) do {} while(0)
-/* #define nat_dbg dbg */
+#endif
+
#define PICO_NAT_TIMEWAIT 240000 /* msec (4 mins) */
#define PICO_NAT_INBOUND 0
@@ -116,8 +120,8 @@ static int nat_cmp_outbound(void *ka, void *kb)
return nat_cmp_proto(a, b);
}
-PICO_TREE_DECLARE(NATOutbound, nat_cmp_outbound);
-PICO_TREE_DECLARE(NATInbound, nat_cmp_inbound);
+static PICO_TREE_DECLARE(NATOutbound, nat_cmp_outbound);
+static PICO_TREE_DECLARE(NATInbound, nat_cmp_inbound);
void pico_ipv4_nat_print_table(void)
{
@@ -366,6 +370,7 @@ static void pico_ipv4_nat_table_cleanup(pico_time now, void *_unused)
pico_ipv4_nat_del(t->nat_port, t->proto);
else
t->conn_active++;
+ break;
default:
/* unknown protocol in NAT table, delete when it has existed NAT_TIMEWAIT */
@@ -378,7 +383,10 @@ static void pico_ipv4_nat_table_cleanup(pico_time now, void *_unused)
nat_dbg("NAT: after table cleanup:\n");
pico_ipv4_nat_print_table();
- pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL);
+ if (!pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL)) {
+ nat_dbg("NAT: Failed to start cleanup timer\n");
+ /* TODO no more NAT table cleanup now */
+ }
}
int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag)
@@ -550,8 +558,13 @@ int pico_ipv4_nat_enable(struct pico_ipv4_link *link)
return -1;
}
+ if (!pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL)) {
+ nat_dbg("NAT: Failed to start cleanup timer\n");
+ return -1;
+ }
+
nat_link = link;
- pico_timer_add(PICO_NAT_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL);
+
return 0;
}
diff --git a/ext/picotcp/modules/pico_nat.h b/ext/picotcp/modules/pico_nat.h
index 5237501..b6d7010 100644
--- a/ext/picotcp/modules/pico_nat.h
+++ b/ext/picotcp/modules/pico_nat.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
diff --git a/ext/picotcp/modules/pico_olsr.c b/ext/picotcp/modules/pico_olsr.c
index e5300a9..b606e5b 100644
--- a/ext/picotcp/modules/pico_olsr.c
+++ b/ext/picotcp/modules/pico_olsr.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
********************************************************************/
@@ -15,7 +15,12 @@
#ifdef PICO_SUPPORT_OLSR
#define DGRAM_MAX_SIZE (100 - 28)
#define MAX_OLSR_MEM (4 * DGRAM_MAX_SIZE)
+
+#ifdef DEBUG_OLSR
+#define olsr_dbg dbg
+#else
#define olsr_dbg(...) do {} while(0)
+#endif
int OOM(void);
@@ -416,7 +421,12 @@ static void olsr_scheduled_output(uint32_t when, void *buffer, uint16_t size, st
p->len = size;
p->pdev = pdev;
buffer_mem_used += DGRAM_MAX_SIZE;
- pico_timer_add(1 + when - ((pico_rand() % OLSR_MAXJITTER)), &olsr_process_out, p);
+ if (!pico_timer_add(1 + when - ((pico_rand() % OLSR_MAXJITTER)), &olsr_process_out, p)) {
+ olsr_dbg("OLSR: Failed to start process timer\n");
+ OOM();
+ PICO_FREE(p);
+ PICO_FREE(buffer);
+ }
}
@@ -1039,7 +1049,10 @@ static void olsr_hello_tick(pico_time when, void *unused)
olsr_make_dgram(d->dev, 0);
d = d->next;
}
- pico_timer_add(OLSR_HELLO_INTERVAL, &olsr_hello_tick, NULL);
+ if (!pico_timer_add(OLSR_HELLO_INTERVAL, &olsr_hello_tick, NULL)) {
+ olsr_dbg("OLSR: Failed to start hello_tick timer\n");
+ /* TODO no more ticks now */
+ }
}
static void olsr_tc_tick(pico_time when, void *unused)
@@ -1052,7 +1065,10 @@ static void olsr_tc_tick(pico_time when, void *unused)
olsr_make_dgram(d->dev, 1);
d = d->next;
}
- pico_timer_add(OLSR_TC_INTERVAL, &olsr_tc_tick, NULL);
+ if (!pico_timer_add(OLSR_TC_INTERVAL, &olsr_tc_tick, NULL)) {
+ olsr_dbg("OLSR: Failed to start tc_tick timer\n");
+ /* TODO no more ticks now */
+ }
}
@@ -1060,6 +1076,7 @@ static void olsr_tc_tick(pico_time when, void *unused)
void pico_olsr_init(void)
{
+ uint32_t hello_timer = 0;
struct pico_ip4 ANY = {
0
};
@@ -1071,8 +1088,15 @@ void pico_olsr_init(void)
pico_socket_bind(udpsock, &ANY, &port);
}
- pico_timer_add(pico_rand() % 100, &olsr_hello_tick, NULL);
- pico_timer_add(pico_rand() % 900, &olsr_tc_tick, NULL);
+ hello_timer = pico_timer_add(pico_rand() % 100, &olsr_hello_tick, NULL);
+ if (!hello_timer) {
+ olsr_dbg("OLSR: Failed to start hello_tick timer\n");
+ return;
+ }
+ if (!pico_timer_add(pico_rand() % 900, &olsr_tc_tick, NULL)) {
+ olsr_dbg("OLSR: Failed to start tc_tick timer\n");
+ pico_timer_cancel(hello_timer);
+ }
}
diff --git a/ext/picotcp/modules/pico_olsr.h b/ext/picotcp/modules/pico_olsr.h
index 11c3bf0..b111188 100644
--- a/ext/picotcp/modules/pico_olsr.h
+++ b/ext/picotcp/modules/pico_olsr.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_posix.c b/ext/picotcp/modules/pico_posix.c
index 4820b12..659cb4c 100644
--- a/ext/picotcp/modules/pico_posix.c
+++ b/ext/picotcp/modules/pico_posix.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Andrei Carp, Maarten Vandersteegen
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_slaacv4.c b/ext/picotcp/modules/pico_slaacv4.c
index f9ac216..7b11cf8 100644
--- a/ext/picotcp/modules/pico_slaacv4.c
+++ b/ext/picotcp/modules/pico_slaacv4.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Bogdan Lupu
*********************************************************************/
@@ -12,6 +12,12 @@
#ifdef PICO_SUPPORT_SLAACV4
+#ifdef DEBUG_SLAACV4
+#define slaacv4_dbg dbg
+#else
+#define slaacv4_dbg(...) do {} while(0)
+#endif
+
#define SLAACV4_NETWORK ((long_be(0xa9fe0000)))
#define SLAACV4_NETMASK ((long_be(0xFFFF0000)))
#define SLAACV4_MINRANGE (0x00000100) /* In host order */
@@ -91,7 +97,9 @@ static void pico_slaacv4_cancel_timers(struct slaacv4_cookie *tmp)
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 };
+ struct pico_ip4 netmask = {
+ 0
+ };
netmask.addr = long_be(0xFFFF0000);
(void)now;
@@ -101,6 +109,12 @@ static void pico_slaacv4_send_announce_timer(pico_time now, void *arg)
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_ANNOUNCE);
tmp->announce_nb++;
tmp->timer = pico_timer_add(ANNOUNCE_INTERVAL * 1000, pico_slaacv4_send_announce_timer, arg);
+ if (!tmp->timer) {
+ slaacv4_dbg("SLAACV4: Failed to start announce timer\n");
+ tmp->state = SLAACV4_ERROR;
+ if (tmp->cb != NULL)
+ tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
+ }
}
else
{
@@ -121,11 +135,23 @@ static void pico_slaacv4_send_probe_timer(pico_time now, void *arg)
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_PROBE);
tmp->probe_try_nb++;
tmp->timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, tmp);
+ if (!tmp->timer) {
+ slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
+ tmp->state = SLAACV4_ERROR;
+ if (tmp->cb != NULL)
+ tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
+ }
}
else
{
tmp->state = SLAACV4_ANNOUNCING;
tmp->timer = pico_timer_add(ANNOUNCE_WAIT * 1000, pico_slaacv4_send_announce_timer, arg);
+ if (!tmp->timer) {
+ slaacv4_dbg("SLAACV4: Failed to start announce timer\n");
+ tmp->state = SLAACV4_ERROR;
+ if (tmp->cb != NULL)
+ tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
+ }
}
}
@@ -140,7 +166,7 @@ static void pico_slaacv4_receive_ipconflict(int reason)
{
if(reason == PICO_ARP_CONFLICT_REASON_CONFLICT)
{
- pico_ipv4_link_del(tmp->device, tmp->ip);
+ pico_ipv4_link_del(tmp->device, tmp->ip);
}
}
@@ -154,6 +180,12 @@ static void pico_slaacv4_receive_ipconflict(int reason)
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_PROBE);
tmp->probe_try_nb++;
tmp->timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, tmp);
+ if (!tmp->timer) {
+ slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
+ tmp->state = SLAACV4_ERROR;
+ if (tmp->cb != NULL)
+ tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
+ }
}
else if (tmp->conflict_nb < MAX_CONFLICTS_FAIL)
{
@@ -163,6 +195,12 @@ static void pico_slaacv4_receive_ipconflict(int reason)
tmp->ip.addr = pico_slaacv4_getip(tmp->device, (uint8_t)1);
pico_arp_register_ipconflict(&tmp->ip, &tmp->device->eth->mac, pico_slaacv4_receive_ipconflict);
tmp->timer = pico_timer_add(RATE_LIMIT_INTERVAL * 1000, pico_slaacv4_send_probe_timer, tmp);
+ if (!tmp->timer) {
+ slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
+ tmp->state = SLAACV4_ERROR;
+ if (tmp->cb != NULL)
+ tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
+ }
}
else
{
@@ -191,12 +229,18 @@ static void pico_slaacv4_hotplug_cb(__attribute__((unused)) struct pico_device *
pico_arp_request(tmp->device, &tmp->ip, PICO_ARP_PROBE);
tmp->probe_try_nb++;
tmp->timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, tmp);
-
+ if (!tmp->timer) {
+ slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
+ tmp->state = SLAACV4_ERROR;
+ if (tmp->cb != NULL)
+ tmp->cb(&tmp->ip, PICO_SLAACV4_ERROR);
+ }
}
else
{
if (tmp->state == SLAACV4_CLAIMED )
pico_ipv4_link_del(tmp->device, tmp->ip);
+
pico_slaacv4_cancel_timers(tmp);
}
}
@@ -212,7 +256,7 @@ int pico_slaacv4_claimip(struct pico_device *dev, void (*cb)(struct pico_ip4 *ip
if( dev->link_state != NULL )
{
- //hotplug detect will work
+ /* hotplug detect will work */
ip.addr = pico_slaacv4_getip(dev, 0);
pico_slaacv4_init_cookie(&ip, dev, &slaacv4_local, cb);
@@ -221,14 +265,6 @@ int pico_slaacv4_claimip(struct pico_device *dev, void (*cb)(struct pico_ip4 *ip
{
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
{
@@ -240,6 +276,11 @@ int pico_slaacv4_claimip(struct pico_device *dev, void (*cb)(struct pico_ip4 *ip
slaacv4_local.state = SLAACV4_CLAIMING;
slaacv4_local.probe_try_nb++;
slaacv4_local.timer = pico_timer_add(PROBE_WAIT * 1000, pico_slaacv4_send_probe_timer, &slaacv4_local);
+ if (!slaacv4_local.timer) {
+ slaacv4_dbg("SLAACV4: Failed to start probe timer\n");
+ slaacv4_local.state = SLAACV4_ERROR;
+ return -1;
+ }
}
return 0;
diff --git a/ext/picotcp/modules/pico_slaacv4.h b/ext/picotcp/modules/pico_slaacv4.h
index d90ef65..8912dbd 100644
--- a/ext/picotcp/modules/pico_slaacv4.h
+++ b/ext/picotcp/modules/pico_slaacv4.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Bogdan Lupu
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_sntp_client.c b/ext/picotcp/modules/pico_sntp_client.c
index 10fa0af..3b68bb2 100644
--- a/ext/picotcp/modules/pico_sntp_client.c
+++ b/ext/picotcp/modules/pico_sntp_client.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Author: Toon Stegen
*********************************************************************/
@@ -17,8 +17,11 @@
#ifdef PICO_SUPPORT_SNTP_CLIENT
+#ifdef DEBUG_SNTP
+#define sntp_dbg dbg
+#else
#define sntp_dbg(...) do {} while(0)
-/* #define sntp_dbg dbg */
+#endif
#define SNTP_VERSION 4
#define PICO_SNTP_MAXBUF (1400)
@@ -91,11 +94,11 @@ static int timestamp_convert(const struct pico_sntp_ts *ts, struct pico_timeval
return -1;
}
- sntp_dbg("Delay: %llu\n", delay);
+ sntp_dbg("Delay: %lu\n", delay);
tv->tv_msec = (pico_time) (((uint32_t)(long_be(ts->frac))) / SNTP_FRAC_TO_PICOSEC + delay);
tv->tv_sec = (pico_time) (long_be(ts->sec) - SNTP_UNIX_OFFSET + (uint32_t)tv->tv_msec / SNTP_THOUSAND);
tv->tv_msec = (uint32_t) (tv->tv_msec & SNTP_BITMASK) % SNTP_THOUSAND;
- sntp_dbg("Converted time stamp: %llusec, %llumsec\n", tv->tv_sec, tv->tv_msec);
+ sntp_dbg("Converted time stamp: %lusec, %lumsec\n", tv->tv_sec, tv->tv_msec);
return 0;
}
@@ -106,6 +109,8 @@ static void pico_sntp_cleanup(struct sntp_server_ns_cookie *ck, pico_err_t statu
if(!ck)
return;
+ pico_timer_cancel(ck->timer);
+
ck->cb_synced(status);
if(ck->sock)
ck->sock->priv = NULL;
@@ -138,7 +143,7 @@ static int pico_sntp_parse(char *buf, struct sntp_server_ns_cookie *ck)
return ret;
}
- sntp_dbg("Server time: %llu seconds and %llu milisecs since 1970\n", server_time.tv_sec, server_time.tv_msec);
+ sntp_dbg("Server time: %lu seconds and %lu milisecs since 1970\n", server_time.tv_sec, server_time.tv_msec);
/* Call back the user saying the time is synced */
pico_sntp_cleanup(ck, PICO_ERR_NOERR);
@@ -171,7 +176,7 @@ static void pico_sntp_client_wakeup(uint16_t ev, struct pico_socket *s)
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);
+ s->priv = NULL; /* make sure UDP callback does not try to read from freed mem again */
PICO_FREE(recvbuf);
}
/* socket is closed */
@@ -220,6 +225,13 @@ static void pico_sntp_send(struct pico_socket *sock, union pico_address *dst)
}
ck->timer = pico_timer_add(5000, sntp_receive_timeout, ck);
+ if (!ck->timer) {
+ sntp_dbg("SNTP: Failed to start timeout timer\n");
+ pico_sntp_cleanup(ck, pico_err);
+ pico_socket_close(sock);
+ pico_socket_del(sock);
+ return;
+ }
header.vn = SNTP_VERSION;
header.mode = SNTP_MODE_CLIENT;
/* header.trs_ts.frac = long_be(0ul); */
@@ -227,25 +239,41 @@ static void pico_sntp_send(struct pico_socket *sock, union pico_address *dst)
pico_socket_sendto(sock, &header, sizeof(header), dst, short_be(sntp_port));
}
+static int pico_sntp_sync_start(struct sntp_server_ns_cookie *ck, union pico_address *addr)
+{
+ uint16_t any_port = 0;
+ struct pico_socket *sock;
+
+ sock = pico_socket_open(ck->proto, PICO_PROTO_UDP, &pico_sntp_client_wakeup);
+ if (!sock)
+ return -1;
+
+ sock->priv = ck;
+ ck->sock = sock;
+ if ((pico_socket_bind(sock, &sntp_inaddr_any, &any_port) < 0)) {
+ pico_socket_close(sock);
+ return -1;
+ }
+ pico_sntp_send(sock, addr);
+
+ return 0;
+}
+
+#ifdef PICO_SUPPORT_DNS_CLIENT
/* used for getting a response from DNS servers */
static void dnsCallback(char *ip, void *arg)
{
struct sntp_server_ns_cookie *ck = (struct sntp_server_ns_cookie *)arg;
union pico_address address;
- struct pico_socket *sock;
int retval = -1;
- uint16_t any_port = 0;
if(!ck) {
sntp_dbg("dnsCallback: Invalid argument\n");
return;
}
- if (0) {
-
- }
+ if(ck->proto == PICO_PROTO_IPV6) {
#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);
@@ -255,11 +283,9 @@ static void dnsCallback(char *ip, void *arg)
retval = -1;
pico_sntp_cleanup(ck, PICO_ERR_ENETDOWN);
}
- }
-
#endif
+ } else if(ck->proto == PICO_PROTO_IPV4) {
#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);
@@ -268,35 +294,23 @@ static void dnsCallback(char *ip, void *arg)
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);
- }
+ retval = pico_sntp_sync_start(ck, &address);
+ if (retval < 0)
+ pico_sntp_cleanup(ck, PICO_ERR_ENOTCONN);
}
}
-
-/* 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;
- }
+#ifdef PICO_SUPPORT_IPV4
+#ifdef PICO_SUPPORT_DNS_CLIENT
+static int pico_sntp_sync_start_dns_ipv4(const char *sntp_server, void (*cb_synced)(pico_err_t status))
+{
+ int retval = -1;
+ struct sntp_server_ns_cookie *ck;
/* IPv4 query */
ck = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
if (!ck) {
@@ -317,7 +331,43 @@ int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status)
strcpy(ck->hostname, sntp_server);
- if(cb_synced == NULL) {
+ ck->cb_synced = cb_synced;
+
+ sntp_dbg("Resolving A %s\n", ck->hostname);
+ retval = pico_dns_client_getaddr(sntp_server, &dnsCallback, ck);
+ if (retval != 0) {
+ PICO_FREE(ck->hostname);
+ PICO_FREE(ck);
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+static int pico_sntp_sync_start_ipv4(union pico_address *addr, void (*cb_synced)(pico_err_t status))
+{
+ int retval = -1;
+ struct sntp_server_ns_cookie *ck;
+ ck = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
+ if (!ck) {
+ pico_err = PICO_ERR_ENOMEM;
+ return -1;
+ }
+
+ ck->proto = PICO_PROTO_IPV4;
+ ck->stamp = 0ull;
+ ck->rec = 0;
+ ck->sock = NULL;
+ /* Set the given IP address as hostname, allocate the maximum IPv4 string length + 1 */
+ ck->hostname = PICO_ZALLOC(15 + 1);
+ if (!ck->hostname) {
+ PICO_FREE(ck);
+ pico_err = PICO_ERR_ENOMEM;
+ return -1;
+ }
+
+ retval = pico_ipv4_to_string(ck->hostname, addr->ip4.addr);
+ if (retval < 0) {
PICO_FREE(ck->hostname);
PICO_FREE(ck);
pico_err = PICO_ERR_EINVAL;
@@ -326,7 +376,22 @@ int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status)
ck->cb_synced = cb_synced;
+ retval = pico_sntp_sync_start(ck, addr);
+ if (retval < 0) {
+ pico_sntp_cleanup(ck, PICO_ERR_ENOTCONN);
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
#ifdef PICO_SUPPORT_IPV6
+#ifdef PICO_SUPPORT_DNS_CLIENT
+static int pico_sntp_sync_start_dns_ipv6(const char *sntp_server, void (*cb_synced)(pico_err_t status))
+{
+ struct sntp_server_ns_cookie *ck6;
+ int retval6 = -1;
/* IPv6 query */
ck6 = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
if (!ck6) {
@@ -356,15 +421,107 @@ int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status)
return -1;
}
+ return 0;
+}
#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);
+static int pico_sntp_sync_start_ipv6(union pico_address *addr, void (*cb_synced)(pico_err_t status))
+{
+ struct sntp_server_ns_cookie *ck6;
+ int retval6 = -1;
+ ck6 = PICO_ZALLOC(sizeof(struct sntp_server_ns_cookie));
+ if (!ck6) {
+ pico_err = PICO_ERR_ENOMEM;
return -1;
}
+ ck6->proto = PICO_PROTO_IPV6;
+ ck6->stamp = 0ull;
+ ck6->rec = 0;
+ ck6->sock = NULL;
+ ck6->cb_synced = cb_synced;
+ /* Set the given IP address as hostname, allocate the maximum IPv6 string length + 1 */
+ ck6->hostname = PICO_ZALLOC(39 + 1);
+ if (!ck6->hostname) {
+ PICO_FREE(ck6);
+ pico_err = PICO_ERR_ENOMEM;
+ return -1;
+ }
+
+ retval6 = pico_ipv6_to_string(ck6->hostname, addr->ip6.addr);
+ if (retval6 < 0) {
+ PICO_FREE(ck6->hostname);
+ PICO_FREE(ck6);
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+ retval6 = pico_sntp_sync_start(ck6, addr);
+ if (retval6 < 0) {
+ pico_sntp_cleanup(ck6, PICO_ERR_ENOTCONN);
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+/* user function to sync the time from a given sntp source in string notation, DNS resolution is needed */
+int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status))
+{
+#ifdef PICO_SUPPORT_DNS_CLIENT
+ int retval4 = -1, retval6 = -1;
+ if (sntp_server == NULL) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+ if(cb_synced == NULL) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+#ifdef PICO_SUPPORT_IPV4
+ retval4 = pico_sntp_sync_start_dns_ipv4(sntp_server, cb_synced);
+#endif
+#ifdef PICO_SUPPORT_IPV6
+ retval6 = pico_sntp_sync_start_dns_ipv6(sntp_server, cb_synced);
+#endif
+
+ if (retval4 != 0 && retval6 != 0)
+ return -1;
+
+ return 0;
+#else
+ sntp_debug("No DNS support available\n");
+ pico_err = PICO_ERR_EPROTONOSUPPORT;
+ return -1;
+#endif
+}
+
+/* user function to sync the time from a given sntp source in pico_address notation */
+int pico_sntp_sync_ip(union pico_address *sntp_addr, void (*cb_synced)(pico_err_t status))
+{
+ int retval4 = -1, retval6 = -1;
+ if (sntp_addr == NULL) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+ if (cb_synced == NULL) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+#ifdef PICO_SUPPORT_IPV4
+ retval4 = pico_sntp_sync_start_ipv4(sntp_addr, cb_synced);
+#endif
+#ifdef PICO_SUPPORT_IPV6
+ retval6 = pico_sntp_sync_start_ipv6(sntp_addr, cb_synced);
+#endif
+
+ if (retval4 != 0 && retval6 != 0)
+ return -1;
+
return 0;
}
@@ -388,7 +545,7 @@ int pico_sntp_gettimeofday(struct pico_timeval *tv)
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);
+ sntp_dbg("Time of day: %lu seconds and %lu milisecs since 1970\n", tv->tv_sec, tv->tv_msec);
return ret;
}
diff --git a/ext/picotcp/modules/pico_sntp_client.h b/ext/picotcp/modules/pico_sntp_client.h
index 79b02b8..21ee5ed 100644
--- a/ext/picotcp/modules/pico_sntp_client.h
+++ b/ext/picotcp/modules/pico_sntp_client.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Author: Toon Stegen
*********************************************************************/
@@ -17,6 +17,7 @@ struct pico_timeval
};
int pico_sntp_sync(const char *sntp_server, void (*cb_synced)(pico_err_t status));
+int pico_sntp_sync_ip(union pico_address *sntp_addr, void (*cb_synced)(pico_err_t status));
int pico_sntp_gettimeofday(struct pico_timeval *tv);
#endif /* _INCLUDE_PICO_SNTP_CLIENT */
diff --git a/ext/picotcp/modules/pico_socket_tcp.c b/ext/picotcp/modules/pico_socket_tcp.c
index ee977b8..569e4e8 100644
--- a/ext/picotcp/modules/pico_socket_tcp.c
+++ b/ext/picotcp/modules/pico_socket_tcp.c
@@ -197,11 +197,11 @@ static int socket_tcp_do_deliver(struct pico_socket *s, struct pico_frame *f)
int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f)
{
struct pico_socket *found = NULL;
+ struct pico_socket *target = NULL;
struct pico_tree_node *index = NULL;
struct pico_tree_node *_tmp;
struct pico_socket *s = NULL;
-
pico_tree_foreach_safe(index, &sp->socks, _tmp){
s = index->keyValue;
/* 4-tuple identification of socket (port-IP) */
@@ -214,10 +214,15 @@ int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f)
}
if (found)
- break;
+ {
+ target = found;
+ if ( found->remote_port != 0)
+ /* only break if it's connected */
+ break;
+ }
} /* FOREACH */
- return socket_tcp_do_deliver(found, f);
+ return socket_tcp_do_deliver(target, f);
}
struct pico_socket *pico_socket_tcp_open(uint16_t family)
diff --git a/ext/picotcp/modules/pico_socket_udp.c b/ext/picotcp/modules/pico_socket_udp.c
index bf3d9f8..9ec1bfd 100644
--- a/ext/picotcp/modules/pico_socket_udp.c
+++ b/ext/picotcp/modules/pico_socket_udp.c
@@ -27,7 +27,24 @@ struct pico_socket *pico_socket_udp_open(void)
}
+#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6)
+static int pico_enqueue_and_wakeup_if_needed(struct pico_queue *q_in, struct pico_socket* s, struct pico_frame* cpy)
+{
+ if (pico_enqueue(q_in, cpy) > 0) {
+ if (s->wakeup){
+ s->wakeup(PICO_SOCK_EV_RD, s);
+ }
+ }
+ else {
+ pico_frame_discard(cpy);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
#ifdef PICO_SUPPORT_IPV4
+#ifdef PICO_SUPPORT_MCAST
static inline int pico_socket_udp_deliver_ipv4_mcast_initial_checks(struct pico_socket *s, struct pico_frame *f)
{
struct pico_ip4 p_dst;
@@ -65,17 +82,12 @@ static int pico_socket_udp_deliver_ipv4_mcast(struct pico_socket *s, struct pico
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);
+ pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
}
return 0;
}
-
+#endif
static int pico_socket_udp_deliver_ipv4_unicast(struct pico_socket *s, struct pico_frame *f)
{
struct pico_frame *cpy;
@@ -84,12 +96,7 @@ static int pico_socket_udp_deliver_ipv4_unicast(struct pico_socket *s, struct pi
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);
- }
+ pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
return 0;
}
@@ -104,7 +111,9 @@ static int pico_socket_udp_deliver_ipv4(struct pico_socket *s, struct pico_frame
s_local.addr = s->local_addr.ip4.addr;
p_dst.addr = ip4hdr->dst.addr;
if ((pico_ipv4_is_broadcast(p_dst.addr)) || pico_ipv4_is_multicast(p_dst.addr)) {
+#ifdef PICO_SUPPORT_MCAST
ret = pico_socket_udp_deliver_ipv4_mcast(s, f);
+#endif
} else if ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr)) {
ret = pico_socket_udp_deliver_ipv4_unicast(s, f);
}
@@ -115,6 +124,7 @@ static int pico_socket_udp_deliver_ipv4(struct pico_socket *s, struct pico_frame
#endif
#ifdef PICO_SUPPORT_IPV6
+#ifdef PICO_SUPPORT_MCAST
static inline int pico_socket_udp_deliver_ipv6_mcast(struct pico_socket *s, struct pico_frame *f)
{
struct pico_ipv6_hdr *ip6hdr;
@@ -137,17 +147,12 @@ static inline int pico_socket_udp_deliver_ipv6_mcast(struct pico_socket *s, stru
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);
+ pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
}
return 0;
}
-
+#endif
static int pico_socket_udp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f)
{
struct pico_ip6 s_local, p_dst;
@@ -157,10 +162,12 @@ static int pico_socket_udp_deliver_ipv6(struct pico_socket *s, struct pico_frame
s_local = s->local_addr.ip6;
p_dst = ip6hdr->dst;
if ((pico_ipv6_is_multicast(p_dst.addr))) {
+#ifdef PICO_SUPPORT_MCAST
int retval = pico_socket_udp_deliver_ipv6_mcast(s, f);
pico_frame_discard(f);
return retval;
- }
+#endif
+ }
else if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || (pico_ipv6_compare(&s_local, &p_dst) == 0))
{ /* Either local socket is ANY, or matches dst */
cpy = pico_frame_copy(f);
@@ -170,10 +177,7 @@ static int pico_socket_udp_deliver_ipv6(struct pico_socket *s, struct pico_frame
return -1;
}
- if (pico_enqueue(&s->q_in, cpy) > 0) {
- if (s->wakeup)
- s->wakeup(PICO_SOCK_EV_RD, s);
- }
+ pico_enqueue_and_wakeup_if_needed(&s->q_in, s, cpy);
}
pico_frame_discard(f);
diff --git a/ext/picotcp/modules/pico_strings.c b/ext/picotcp/modules/pico_strings.c
index 974126f..cb60650 100644
--- a/ext/picotcp/modules/pico_strings.c
+++ b/ext/picotcp/modules/pico_strings.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2015 Altran ISY BeNeLux. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2015-2017 Altran ISY BeNeLux. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
@@ -36,7 +36,7 @@ int pico_strncasecmp(const char *const str1, const char *const str2, size_t n)
if ((!ch1) && (!ch2))
return 0;
}
- return 1;
+ return 0;
}
size_t pico_strnlen(const char *str, size_t n)
diff --git a/ext/picotcp/modules/pico_strings.h b/ext/picotcp/modules/pico_strings.h
index 9a6209d..c4b6ac2 100644
--- a/ext/picotcp/modules/pico_strings.h
+++ b/ext/picotcp/modules/pico_strings.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2015 Altran ISY BeNeLux. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2015-2017 Altran ISY BeNeLux. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
diff --git a/ext/picotcp/modules/pico_tcp.c b/ext/picotcp/modules/pico_tcp.c
index 67e3c73..df1c9ea 100644
--- a/ext/picotcp/modules/pico_tcp.c
+++ b/ext/picotcp/modules/pico_tcp.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -13,6 +13,7 @@
#include "pico_socket.h"
#include "pico_stack.h"
#include "pico_socket.h"
+#include "pico_socket_tcp.h"
#include "pico_queue.h"
#include "pico_tree.h"
@@ -42,8 +43,6 @@
#define ONE_GIGABYTE ((uint32_t)(1024UL * 1024UL * 1024UL))
-/* check if the Nagle algorithm is enabled on the socket */
-#define IS_NAGLE_ENABLED(s) (!(!(!(s->opt_flags & (1u << PICO_SOCKET_OPT_TCPNODELAY)))))
/* check if tcp connection is "idle" according to Nagle (RFC 896) */
#define IS_TCP_IDLE(t) ((t->in_flight == 0) && (t->tcpq_out.size == 0))
/* check if the hold queue contains data (again Nagle) */
@@ -54,12 +53,24 @@
#ifdef PICO_SUPPORT_TCP
-#define tcp_dbg_nagle(...) do {} while(0)
+
+#ifdef DEBUG_TCP_GENERAL
+#define tcp_dbg dbg
+#else
+#define tcp_dbg(...) do {} while(0)
+#endif
+
+#ifdef DEBUG_TCP_NAGLE
+#define tcp_dbg_nagle dbg
+#else
+#define tcp_dbg_nagle(...) do {} while(0)
+#endif
+
+#ifdef DEBUG_TCP_OPTIONS
+#define tcp_dbg_options dbg
+#else
#define tcp_dbg_options(...) do {} while(0)
-
-
-#define tcp_dbg(...) do {} while(0)
-/* #define tcp_dbg dbg */
+#endif
#ifdef PICO_SUPPORT_MUTEX
static void *Mutex = NULL;
@@ -85,8 +96,13 @@ static int input_segment_compare(void *ka, void *kb)
static struct tcp_input_segment *segment_from_frame(struct pico_frame *f)
{
- struct tcp_input_segment *seg = PICO_ZALLOC(sizeof(struct tcp_input_segment));
- if ((!seg) || (!f->payload_len))
+ struct tcp_input_segment *seg;
+
+ if (!f->payload_len)
+ return NULL;
+
+ seg = PICO_ZALLOC(sizeof(struct tcp_input_segment));
+ if (!seg)
return NULL;
seg->payload = PICO_ZALLOC(f->payload_len);
@@ -132,7 +148,9 @@ static void *peek_segment(struct pico_tcp_queue *tq, uint32_t seq)
}
else
{
- struct tcp_input_segment dummy = { 0 };
+ struct tcp_input_segment dummy = {
+ 0
+ };
dummy.seq = seq;
return pico_tree_findKey(&tq->pool, &dummy);
@@ -356,7 +374,7 @@ static int release_until(struct pico_tcp_queue *q, uint32_t seq)
if (seq_result <= 0)
{
head = next_segment(q, cur);
- //tcp_dbg("Releasing %08x, len: %d\n", SEQN((struct pico_frame *)head), ((struct pico_frame *)head)->payload_len);
+ /* tcp_dbg("Releasing %08x, len: %d\n", SEQN((struct pico_frame *)head), ((struct pico_frame *)head)->payload_len); */
pico_discard_segment(q, cur);
ret++;
} else {
@@ -974,7 +992,9 @@ static void sock_stats(uint32_t when, void *arg)
struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
tcp_dbg("STATISTIC> [%lu] socket state: %02x --> local port:%d remote port: %d queue size: %d snd_una: %08x snd_nxt: %08x cwnd: %d\n",
when, t->sock.state, short_be(t->sock.local_port), short_be(t->sock.remote_port), t->tcpq_out.size, SEQN((struct pico_frame *)first_segment(&t->tcpq_out)), t->snd_nxt, t->cwnd);
- pico_timer_add(2000, sock_stats, t);
+ if (!pico_timer_add(2000, sock_stats, t)) {
+ tcp_dbg("TCP: Failed to start socket statistics timer\n");
+ }
}
#endif
@@ -990,6 +1010,7 @@ static void pico_tcp_keepalive(pico_time now, void *arg)
tcp_send_probe(t);
t->ka_retries_count++;
}
+
if (t->ka_retries_count > t->ka_probes) {
if (t->sock.wakeup)
{
@@ -997,7 +1018,8 @@ static void pico_tcp_keepalive(pico_time now, void *arg)
t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
}
}
- if (((t->ka_retries_count * t->ka_intvl) + t->ka_time) < (now - t->ack_timestamp)) {
+
+ if (((t->ka_retries_count * (pico_time)t->ka_intvl) + t->ka_time) < (now - t->ack_timestamp)) {
/* Next probe */
tcp_send_probe(t);
t->ka_retries_count++;
@@ -1006,7 +1028,13 @@ static void pico_tcp_keepalive(pico_time now, void *arg)
t->ka_retries_count = 0;
}
}
+
t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t);
+ if (!t->keepalive_tmr) {
+ tcp_dbg("TCP: Failed to start keepalive timer\n");
+ if (t->sock.wakeup)
+ t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
+ }
}
static inline void rto_set(struct pico_socket_tcp *t, uint32_t rto)
@@ -1049,10 +1077,19 @@ struct pico_socket *pico_tcp_open(uint16_t family)
#ifdef PICO_TCP_SUPPORT_SOCKET_STATS
- pico_timer_add(2000, sock_stats, t);
+ if (!pico_timer_add(2000, sock_stats, t)) {
+ tcp_dbg("TCP: Failed to start socket statistics timer\n");
+ PICO_FREE(t);
+ return NULL;
+ }
#endif
t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t);
+ if (!t->keepalive_tmr) {
+ tcp_dbg("TCP: Failed to start keepalive timer\n");
+ PICO_FREE(t);
+ return NULL;
+ }
tcp_set_space(t);
return &t->sock;
@@ -1158,6 +1195,7 @@ static void initconn_retry(pico_time when, void *arg)
pico_err = PICO_ERR_ECONNREFUSED;
t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
}
+
pico_socket_del(&t->sock);
return;
}
@@ -1177,7 +1215,7 @@ int pico_tcp_initconn(struct pico_socket *s)
struct pico_tcp_hdr *hdr;
uint16_t mtu, opt_len = tcp_options_size(ts, PICO_TCP_SYN);
- syn = s->net->alloc(s->net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
+ syn = s->net->alloc(s->net, NULL, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
if (!syn)
return -1;
@@ -1206,8 +1244,13 @@ int pico_tcp_initconn(struct pico_socket *s)
/* TCP: ENQUEUE to PROTO ( SYN ) */
tcp_dbg("Sending SYN... (ports: %d - %d) size: %d\n", short_be(ts->sock.local_port), short_be(ts->sock.remote_port), syn->buffer_len);
- pico_enqueue(&tcp_out, syn);
ts->retrans_tmr = pico_timer_add(PICO_TCP_SYN_TO << ts->backoff, initconn_retry, ts);
+ if (!ts->retrans_tmr) {
+ tcp_dbg("TCP: Failed to start initconn_retry timer\n");
+ PICO_FREE(syn);
+ return -1;
+ }
+ pico_enqueue(&tcp_out, syn);
return 0;
}
@@ -1218,7 +1261,7 @@ static int tcp_send_synack(struct pico_socket *s)
struct pico_tcp_hdr *hdr;
uint16_t opt_len = tcp_options_size(ts, PICO_TCP_SYN | PICO_TCP_ACK);
- synack = s->net->alloc(s->net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
+ synack = s->net->alloc(s->net, NULL, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
if (!synack)
return -1;
@@ -1245,7 +1288,7 @@ static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags, int is_kee
struct pico_frame *f;
struct pico_tcp_hdr *hdr;
uint16_t opt_len = tcp_options_size(t, flags);
- f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
+ f = t->sock.net->alloc(t->sock.net, NULL, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
if (!f) {
return;
}
@@ -1295,10 +1338,11 @@ static int tcp_do_send_rst(struct pico_socket *s, uint32_t seq)
uint16_t opt_len = tcp_options_size(t, PICO_TCP_RST);
struct pico_frame *f;
struct pico_tcp_hdr *hdr;
- f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
+ f = t->sock.net->alloc(t->sock.net, NULL, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
if (!f) {
return -1;
}
+
f->sock = &t->sock;
tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n");
@@ -1333,13 +1377,13 @@ static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr)
if (fr && ((s->state & PICO_SOCKET_STATE_TCP) > PICO_SOCKET_STATE_TCP_SYN_RECV)) {
/* in synchronized state: send RST with seq = ack from previous segment */
hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr;
- ret = tcp_do_send_rst(s, hdr_rcv->ack);
+ ret = tcp_do_send_rst(s, hdr_rcv->ack);
} else {
/* non-synchronized state */
/* go to CLOSED here to prevent timer callback to go on after timeout */
(t->sock).state &= 0x00FFU;
(t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
- ret = tcp_do_send_rst(s, long_be(t->snd_nxt));
+ ret = tcp_do_send_rst(s, long_be(t->snd_nxt));
/* Set generic socket state to CLOSED, too */
(t->sock).state &= 0xFF00U;
@@ -1352,6 +1396,7 @@ static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr)
/* delete socket */
pico_socket_del(&t->sock);
}
+
return ret;
}
@@ -1409,7 +1454,7 @@ int pico_tcp_reply_rst(struct pico_frame *fr)
tcp_dbg("TCP> sending RST ... \n");
- f = fr->sock->net->alloc(fr->sock->net, size);
+ f = fr->sock->net->alloc(fr->sock->net, NULL, size);
if (!f) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -1457,7 +1502,7 @@ static int tcp_nosync_rst(struct pico_socket *s, struct pico_frame *fr)
/***************************************************************************/
/* sending RST */
- f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
+ f = t->sock.net->alloc(t->sock.net, NULL, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
if (!f) {
return -1;
@@ -1505,6 +1550,10 @@ static void tcp_linger(struct pico_socket_tcp *t)
{
pico_timer_cancel(t->fin_tmr);
t->fin_tmr = pico_timer_add(t->linger_timeout, tcp_deltcb, t);
+ if (!t->fin_tmr) {
+ tcp_dbg("TCP: failed to start delete callback timer, deleting socket now\n");
+ tcp_deltcb((pico_time)0, t);
+ }
}
static void tcp_send_fin(struct pico_socket_tcp *t)
@@ -1512,7 +1561,7 @@ static void tcp_send_fin(struct pico_socket_tcp *t)
struct pico_frame *f;
struct pico_tcp_hdr *hdr;
uint16_t opt_len = tcp_options_size(t, PICO_TCP_FIN);
- f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
+ f = t->sock.net->alloc(t->sock.net, NULL, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
if (!f) {
return;
}
@@ -1541,6 +1590,7 @@ static void tcp_send_fin(struct pico_socket_tcp *t)
} else {
pico_frame_discard(f);
}
+
tcp_linger(t);
}
@@ -1932,7 +1982,12 @@ static void add_retransmission_timer(struct pico_socket_tcp *t, pico_time next_t
if (!t->retrans_tmr) {
t->retrans_tmr = pico_timer_add(t->retrans_tmr_due - now, tcp_retrans_timeout, t);
- tcp_dbg("Next timeout in %u msec\n", (uint32_t) (t->retrans_tmr_due - now));
+ if(!t->retrans_tmr) {
+ tcp_dbg("TCP: Failed to start retransmission timer\n");
+ //TODO do something about this?
+ } else {
+ tcp_dbg("Next timeout in %u msec\n", (uint32_t) (t->retrans_tmr_due - now));
+ }
}
}
@@ -2018,12 +2073,19 @@ static int tcp_ack(struct pico_socket *s, struct pico_frame *f)
{
struct pico_frame *f_new; /* use with Nagle to push to out queue */
struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
- struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+ struct pico_tcp_hdr *hdr;
uint32_t rtt = 0;
uint16_t acked = 0;
pico_time acked_timestamp = 0;
-
struct pico_frame *una = NULL;
+
+ if (!f || !s) {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+ hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+
if ((hdr->flags & PICO_TCP_ACK) == 0)
return -1;
@@ -2066,7 +2128,7 @@ static int tcp_ack(struct pico_socket *s, struct pico_frame *f)
/* Do rtt/rttvar/rto calculations */
/* First, try with timestamps, using the value from options */
- if(f && (f->timestamp != 0)) {
+ if(f->timestamp != 0) {
rtt = time_diff(TCP_TIME, f->timestamp);
if (rtt)
tcp_rtt(t, rtt);
@@ -2201,7 +2263,7 @@ static int tcp_finwaitack(struct pico_socket *s, struct pico_frame *f)
/* acking part */
tcp_ack(s, f);
-
+
tcp_dbg("FIN_WAIT1: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt);
if (ACKN(f) == (t->snd_nxt - 1u)) {
/* update TCP state */
@@ -2209,6 +2271,7 @@ static int tcp_finwaitack(struct pico_socket *s, struct pico_frame *f)
s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT2;
tcp_dbg("TCP> IN STATE FIN_WAIT2\n");
}
+
return 0;
}
@@ -2218,8 +2281,8 @@ static void tcp_deltcb(pico_time when, void *arg)
IGNORE_PARAMETER(when);
/* send RST if not yet in TIME_WAIT */
- if ( (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_TIME_WAIT)
- && (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSING) ) {
+ if ((((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_TIME_WAIT)
+ && (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSING)) {
tcp_dbg("Called deltcb in state = %04x (sending reset!)\n", (t->sock).state);
tcp_do_send_rst(&t->sock, long_be(t->snd_nxt));
} else {
@@ -2279,6 +2342,7 @@ static int tcp_closing_ack(struct pico_socket *s, struct pico_frame *f)
/* set timer */
tcp_linger(t);
}
+
return 0;
}
@@ -2298,6 +2362,7 @@ static int tcp_lastackwait(struct pico_socket *s, struct pico_frame *f)
/* delete socket */
pico_socket_del(s);
}
+
return 0;
}
@@ -2315,7 +2380,10 @@ static int tcp_syn(struct pico_socket *s, struct pico_frame *f)
return -1;
#ifdef PICO_TCP_SUPPORT_SOCKET_STATS
- pico_timer_add(2000, sock_stats, s);
+ if (!pico_timer_add(2000, sock_stats, s)) {
+ tcp_dbg("TCP: Failed to start socket statistics timer\n");
+ return -1;
+ }
#endif
new->sock.remote_port = ((struct pico_trans *)f->transport_hdr)->sport;
@@ -2334,9 +2402,9 @@ static int tcp_syn(struct pico_socket *s, struct pico_frame *f)
#endif
f->sock = &new->sock;
- tcp_parse_options(f);
mtu = (uint16_t)pico_socket_get_mss(&new->sock);
new->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
+ tcp_parse_options(f);
new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
new->tcpq_hold.max_size = 2u * mtu;
@@ -2579,7 +2647,7 @@ static void tcp_force_closed(struct pico_socket *s)
} else {
pico_err = PICO_ERR_ECONNRESET;
if ((t->sock).wakeup)
- (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock));
+ (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock));
/* delete socket */
pico_socket_del(&t->sock);
@@ -2798,8 +2866,8 @@ static struct pico_frame *tcp_split_segment(struct pico_socket_tcp *t, struct pi
size1 = size;
size2 = (uint16_t)(size_f - size);
- f1 = pico_socket_frame_alloc(&t->sock, (uint16_t) (size1 + overhead));
- f2 = pico_socket_frame_alloc(&t->sock, (uint16_t) (size2 + overhead));
+ f1 = pico_socket_frame_alloc(&t->sock, get_sock_dev(&t->sock), (uint16_t) (size1 + overhead));
+ f2 = pico_socket_frame_alloc(&t->sock, get_sock_dev(&t->sock), (uint16_t) (size2 + overhead));
if (!f1 || !f2) {
pico_err = PICO_ERR_ENOMEM;
@@ -2836,7 +2904,10 @@ static struct pico_frame *tcp_split_segment(struct pico_socket_tcp *t, struct pi
pico_discard_segment(&t->tcpq_out, f);
/* Enqueue f2 for later send... */
- pico_enqueue_segment(&t->tcpq_out, f2);
+ if (pico_enqueue_segment(&t->tcpq_out, f2) < 0) {
+ tcp_dbg("Discarding invalid segment\n");
+ pico_frame_discard(f2);
+ }
/* Return the partial frame */
return f1;
@@ -2944,7 +3015,7 @@ static struct pico_frame *pico_hold_segment_make(struct pico_socket_tcp *t)
break;
}
/* alloc new frame with payload size = off + total_len */
- f_new = pico_socket_frame_alloc(s, (uint16_t)(off + total_len));
+ f_new = pico_socket_frame_alloc(s, get_sock_dev(s), (uint16_t)(off + total_len));
if (!f_new) {
pico_err = PICO_ERR_ENOMEM;
return f_new;
diff --git a/ext/picotcp/modules/pico_tcp.h b/ext/picotcp/modules/pico_tcp.h
index ece38bb..784a6bd 100644
--- a/ext/picotcp/modules/pico_tcp.h
+++ b/ext/picotcp/modules/pico_tcp.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
diff --git a/ext/picotcp/modules/pico_tftp.c b/ext/picotcp/modules/pico_tftp.c
index 83c5c6f..e20a06c 100644
--- a/ext/picotcp/modules/pico_tftp.c
+++ b/ext/picotcp/modules/pico_tftp.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -13,6 +13,12 @@
#include
#include
+#ifdef DEBUG_TFTP
+#define tftp_dbg dbg
+#else
+#define tftp_dbg(...) do {} while(0)
+#endif
+
/* a zero value means adaptative timeout! (2, 4, 8) */
#define PICO_TFTP_TIMEOUT 2000U
@@ -291,6 +297,7 @@ static inline int do_callback(struct pico_tftp_session *session, uint16_t err, u
}
static void timer_callback(pico_time now, void *arg);
+static void tftp_finish(struct pico_tftp_session *session);
static void tftp_schedule_timeout(struct pico_tftp_session *session, pico_time interval)
{
@@ -299,10 +306,20 @@ static void tftp_schedule_timeout(struct pico_tftp_session *session, pico_time i
if (session->active_timers) {
if (session->bigger_wallclock > new_timeout) {
session->timer = pico_timer_add(interval + 1, timer_callback, session);
+ if (!session->timer) {
+ tftp_dbg("TFTP: Failed to start callback timer, deleting session\n");
+ tftp_finish(session);
+ return;
+ }
session->active_timers++;
}
} else {
session->timer = pico_timer_add(interval + 1, timer_callback, session);
+ if (!session->timer) {
+ tftp_dbg("TFTP: Failed to start callback timer, deleting session\n");
+ tftp_finish(session);
+ return;
+ }
session->active_timers++;
session->bigger_wallclock = new_timeout;
}
@@ -389,7 +406,9 @@ static void tftp_send_oack(struct pico_tftp_session *session)
size_t options_size;
size_t options_pos = sizeof(struct pico_tftp_hdr);
uint8_t *buf;
- char str_options[MAX_OPTIONS_SIZE] = { 0 };
+ char str_options[MAX_OPTIONS_SIZE] = {
+ 0
+ };
options_size = prepare_options_string(session, str_options, session->file_size);
@@ -419,7 +438,9 @@ static void tftp_send_req(struct pico_tftp_session *session, union pico_address
size_t options_size;
size_t options_pos;
uint8_t *buf;
- char str_options[MAX_OPTIONS_SIZE] = { 0 };
+ char str_options[MAX_OPTIONS_SIZE] = {
+ 0
+ };
if (!filename) {
return;
@@ -1263,6 +1284,7 @@ int32_t pico_tftp_get(struct pico_tftp_session *session, uint8_t *data, int32_t
pico_err = PICO_ERR_EINVAL;
return -1;
}
+
synchro = *(int*)session->argument;
*(int*)session->argument = 0;
if ((session->state != TFTP_STATE_RX) && (session->state != TFTP_STATE_READ_REQUESTED))
@@ -1287,6 +1309,7 @@ int32_t pico_tftp_put(struct pico_tftp_session *session, uint8_t *data, int32_t
pico_err = PICO_ERR_EINVAL;
return -1;
}
+
synchro = *(int*)session->argument;
*(int*)session->argument = 0;
if (synchro < 0)
diff --git a/ext/picotcp/modules/pico_tftp.h b/ext/picotcp/modules/pico_tftp.h
index bcbb648..2785189 100644
--- a/ext/picotcp/modules/pico_tftp.h
+++ b/ext/picotcp/modules/pico_tftp.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
*********************************************************************/
diff --git a/ext/picotcp/modules/pico_udp.c b/ext/picotcp/modules/pico_udp.c
index 9545781..6d3ba72 100644
--- a/ext/picotcp/modules/pico_udp.c
+++ b/ext/picotcp/modules/pico_udp.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -14,8 +14,13 @@
#include "pico_socket.h"
#include "pico_stack.h"
-#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
+#ifdef DEBUG_UDP
+#define udp_dbg dbg
+#else
#define udp_dbg(...) do {} while(0)
+#endif
+
+#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
/* Queues */
static struct pico_queue udp_in = {
diff --git a/ext/picotcp/modules/pico_udp.h b/ext/picotcp/modules/pico_udp.h
index 80b97a9..7c5814e 100644
--- a/ext/picotcp/modules/pico_udp.h
+++ b/ext/picotcp/modules/pico_udp.h
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
diff --git a/ext/picotcp/rules/6lowpan.mk b/ext/picotcp/rules/6lowpan.mk
new file mode 100644
index 0000000..58bbf69
--- /dev/null
+++ b/ext/picotcp/rules/6lowpan.mk
@@ -0,0 +1,62 @@
+OPTIONS+=-DPICO_SUPPORT_6LOWPAN -DPICO_SUPPORT_IPV6
+
+################################################################################
+# DEFAULTS
+################################################################################
+
+# Enable the 6LoWPAN IPHC compression scheme by default
+6LOWPAN_IPHC?=1
+
+# Disable MAC framing for mac-enabled radios, disabled by default
+6LOWPAN_NOMAC?=0
+
+# Enable IEEE802.15.4 device support by default
+IEEE802154?=1
+
+# Enable radiotest packet dump
+RADIOTEST_PCAP?=0
+
+################################################################################
+# 6LOWPAN OPTIONS
+################################################################################
+
+ifeq ($(6LOWPAN_IPHC), 1)
+ EXTRA_CFLAGS+=-DPICO_6LOWPAN_IPHC_ENABLED
+endif
+
+ifeq ($(6LOWPAN_NOMAC), 1)
+ EXTRA_CFLAGS+=-DPICO_6LOWPAN_NOMAC
+endif
+
+################################################################################
+# 6LOWPAN LINK LAYER OPTIONS
+################################################################################
+
+# IEEE802.15.4 with or without mac layer
+ifeq ($(IEEE802154), 1)
+ 6LOWPAN_OPTIONS+=-DPICO_SUPPORT_802154
+ POSIX_OBJ+=modules/pico_dev_radiotest.o \
+ modules/pico_dev_radio_mgr.o
+endif
+
+OPTIONS+=$(6LOWPAN_OPTIONS)
+
+# Append module objects
+MOD_OBJ+=$(LIBBASE)modules/pico_6lowpan_ll.o
+MOD_OBJ+=$(LIBBASE)modules/pico_6lowpan.o
+MOD_OBJ+=$(LIBBASE)modules/pico_802154.o
+
+# Count the amount of supported 6LoWPAN Link Layer protocols based on the amount of words in
+# $6LOWPAN_OPTIONS. This allows us to define a static array that can be initialized with the 6LoWPAN
+# link layer protocol definitions for the supported link layer protocols. This happens upon
+# initialization of the 6LoWPAN_LL-layer.
+EXTRA_CFLAGS+=-DPICO_6LOWPAN_LLS=$(words $(6LOWPAN_OPTIONS))
+
+################################################################################
+# RADIOTEST
+################################################################################
+
+ifeq ($(RADIOTEST_PCAP), 1)
+ EXTRA_CFLAGS+=-DRADIO_PCAP
+ TEST_LDFLAGS+=-lpcap
+endif
diff --git a/ext/picotcp/rules/debug.mk b/ext/picotcp/rules/debug.mk
new file mode 100644
index 0000000..481c45d
--- /dev/null
+++ b/ext/picotcp/rules/debug.mk
@@ -0,0 +1,207 @@
+DEBUG_ALL?=0
+
+DEBUG_ARP?=0
+DEBUG_AODV?=0
+DEBUG_PPP?=0
+DEBUG_TAP_ALL?=0
+DEBUG_TAP_GENERAL?=0
+DEBUG_TAP_INFO?=0
+DEBUG_TAP_WIN?=0
+DEBUG_TAP_REG?=0
+DEBUG_DHCP_CLIENT?=0
+DEBUG_DHCP_SERVER?=0
+DEBUG_DNS?=0
+DEBUG_DNS_SD?=0
+DEBUG_FRAG?=0
+DEBUG_IGMP?=0
+DEBUG_IPF?=0
+DEBUG_MCAST?=0
+DEBUG_IPV6?=0
+DEBUG_IPV6_ROUTE?=0
+DEBUG_IPV6_ND?=0
+DEBUG_MDNS?=0
+DEBUG_MLD?=0
+DEBUG_MM?=0
+DEBUG_NAT?=0
+DEBUG_OLSR?=0
+DEBUG_SLAACV4?=0
+DEBUG_SNTP?=0
+DEBUG_TCP_ALL?=0
+DEBUG_TCP_NAGLE?=0
+DEBUG_TCP_OPTIONS?=0
+DEBUG_TCP_GENERAL?=0
+DEBUG_TFTP?=0
+DEBUG_UDP?=0
+DEBUG_6LOWPAN?=0
+DEBUG_RADIOTEST?=0
+
+ifneq ($(DEBUG_ALL),0)
+ DEBUG_ARP=1
+ DEBUG_AODV=1
+ DEBUG_PPP=1
+ DEBUG_TAP_ALL=1
+ DEBUG_DHCP_CLIENT=1
+ DEBUG_DHCP_SERVER=1
+ DEBUG_DNS=1
+ DEBUG_DNS_SD=1
+ DEBUG_FRAG=1
+ DEBUG_IGMP=1
+ DEBUG_IPF=1
+ DEBUG_MCAST=1
+ DEBUG_IPV6=1
+ DEBUG_IPV6_ROUTE=1
+ DEBUG_IPV6_ND=1
+ DEBUG_MDNS=1
+ DEBUG_MLD=1
+ DEBUG_MM=1
+ DEBUG_NAT=1
+ DEBUG_OLSR=1
+ DEBUG_SLAACV4=1
+ DEBUG_SNTP=1
+ DEBUG_TCP_ALL=1
+ DEBUG_TFTP=1
+ DEBUG_UDP=1
+ DEBUG_6LOWPAN=1
+ DEBUG_RADIOTEST=1
+endif
+
+ifneq ($(DEBUG_TCP_ALL),0)
+ DEBUG_TCP_NAGLE=1
+ DEBUG_TCP_OPTIONS=1
+ DEBUG_TCP_GENERAL=1
+endif
+
+ifneq ($(DEBUG_TAP_ALL),0)
+ DEBUG_TAP_GENERAL=1
+ DEBUG_TAP_INFO=1
+ DEBUG_TAP_WIN=1
+ DEBUG_TAP_REG=1
+endif
+
+ifneq ($(DEBUG_ARP),0)
+ CFLAGS+=-DDEBUG_ARP
+endif
+
+ifneq ($(DEBUG_AODV),0)
+ CFLAGS+=-DDEBUG_AODV
+endif
+
+ifneq ($(DEBUG_PPP),0)
+ CFLAGS+=-DDEBUG_PPP
+endif
+
+ifneq ($(DEBUG_TAP_GENERAL),0)
+ CFLAGS+=-DDEBUG_TAP_GENERAL
+endif
+
+ifneq ($(DEBUG_TAP_INFO),0)
+ CFLAGS+=-DDEBUG_TAP_INFO
+endif
+
+ifneq ($(DEBUG_TAP_WIN),0)
+ CFLAGS+=-DDEBUG_TAP_WIN
+endif
+
+ifneq ($(DEBUG_TAP_REG),0)
+ CFLAGS+=-DDEBUG_TAP_REG
+endif
+
+ifneq ($(DEBUG_DHCP_CLIENT),0)
+ CFLAGS+=-DDEBUG_DHCP_CLIENT
+endif
+
+ifneq ($(DEBUG_DHCP_SERVER),0)
+ CFLAGS+=-DDEBUG_DHCP_SERVER
+endif
+
+ifneq ($(DEBUG_DNS),0)
+ CFLAGS+=-DDEBUG_DNS
+endif
+
+ifneq ($(DEBUG_DNS_SD),0)
+ CFLAGS+=-DDEBUG_DNS_SD
+endif
+
+ifneq ($(DEBUG_FRAG),0)
+ CFLAGS+=-DDEBUG_FRAG
+endif
+
+ifneq ($(DEBUG_IGMP),0)
+ CFLAGS+=-DDEBUG_IGMP
+endif
+
+ifneq ($(DEBUG_IPF),0)
+ CFLAGS+=-DDEBUG_IPF
+endif
+
+ifneq ($(DEBUG_MCAST),0)
+ CFLAGS+=-DDEBUG_MCAST
+endif
+
+ifneq ($(DEBUG_IPV6),0)
+ CFLAGS+=-DDEBUG_IPV6
+endif
+
+ifneq ($(DEBUG_IPV6_ROUTE),0)
+ CFLAGS+=-DDEBUG_IPV6_ROUTE
+endif
+
+ifneq ($(DEBUG_IPV6_ND),0)
+ CFLAGS+=-DDEBUG_IPV6_ND
+endif
+
+ifneq ($(DEBUG_MDNS),0)
+ CFLAGS+=-DDEBUG_MDNS
+endif
+
+ifneq ($(DEBUG_MLD),0)
+ CFLAGS+=-DDEBUG_MLD
+endif
+
+ifneq ($(DEBUG_MM),0)
+ CFLAGS+=-DDEBUG_MM
+endif
+
+ifneq ($(DEBUG_NAT),0)
+ CFLAGS+=-DDEBUG_NAT
+endif
+
+ifneq ($(DEBUG_OLSR),0)
+ CFLAGS+=-DDEBUG_OLSR
+endif
+
+ifneq ($(DEBUG_SLAACV4),0)
+ CFLAGS+=-DDEBUG_SLAACV4
+endif
+
+ifneq ($(DEBUG_SNTP),0)
+ CFLAGS+=-DDEBUG_SNTP
+endif
+
+ifneq ($(DEBUG_TCP_NAGLE),0)
+ CFLAGS+=-DDEBUG_TCP_NAGLE
+endif
+
+ifneq ($(DEBUG_TCP_OPTIONS),0)
+ CFLAGS+=-DDEBUG_TCP_OPTIONS
+endif
+
+ifneq ($(DEBUG_TCP_GENERAL),0)
+ CFLAGS+=-DDEBUG_TCP_GENERAL
+endif
+
+ifneq ($(DEBUG_TFTP),0)
+ CFLAGS+=-DDEBUG_TFTP
+endif
+
+ifneq ($(DEBUG_UDP),0)
+ CFLAGS+=-DDEBUG_UDP
+endif
+
+ifneq ($(DEBUG_6LOWPAN),0)
+ CFLAGS+=-DDEBUG_6LOWPAN
+endif
+
+ifneq ($(DEBUG_RADIOTEST), 0)
+ CFLAGS+=-DDEBUG_RADIOTEST
+endif
diff --git a/ext/picotcp/rules/eth.mk b/ext/picotcp/rules/eth.mk
index f955bd8..50b598a 100644
--- a/ext/picotcp/rules/eth.mk
+++ b/ext/picotcp/rules/eth.mk
@@ -1,2 +1,3 @@
OPTIONS+=-DPICO_SUPPORT_ETH
MOD_OBJ+=$(LIBBASE)modules/pico_arp.o
+MOD_OBJ+=$(LIBBASE)modules/pico_ethernet.o
diff --git a/ext/picotcp/rules/ipc.mk b/ext/picotcp/rules/ipc.mk
new file mode 100644
index 0000000..61c5ed2
--- /dev/null
+++ b/ext/picotcp/rules/ipc.mk
@@ -0,0 +1 @@
+MOD_OBJ+=$(LIBBASE)modules/pico_dev_ipc.o
diff --git a/ext/picotcp/rules/mcast.mk b/ext/picotcp/rules/mcast.mk
index 037305b..5628a7b 100644
--- a/ext/picotcp/rules/mcast.mk
+++ b/ext/picotcp/rules/mcast.mk
@@ -1 +1,2 @@
OPTIONS+=-DPICO_SUPPORT_MCAST
+MOD_OBJ+=$(LIBBASE)modules/pico_mcast.o
diff --git a/ext/picotcp/stack/pico_device.c b/ext/picotcp/stack/pico_device.c
index b76d4f3..4d06f1a 100644
--- a/ext/picotcp/stack/pico_device.c
+++ b/ext/picotcp/stack/pico_device.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -16,6 +16,10 @@
#include "pico_ipv4.h"
#include "pico_icmp6.h"
#include "pico_eth.h"
+#include "pico_802154.h"
+#include "pico_6lowpan.h"
+#include "pico_6lowpan_ll.h"
+#include "pico_addressing.h"
#define PICO_DEVICE_DEFAULT_MTU (1500)
struct pico_devices_rr_info {
@@ -32,6 +36,7 @@ static int pico_dev_cmp(void *ka, void *kb)
if (a->hash < b->hash)
return -1;
+
if (a->hash > b->hash)
return 1;
@@ -40,6 +45,43 @@ static int pico_dev_cmp(void *ka, void *kb)
PICO_TREE_DECLARE(Device_tree, pico_dev_cmp);
+#ifdef PICO_SUPPORT_6LOWPAN
+static struct pico_ipv6_link * pico_6lowpan_link_add(struct pico_device *dev, const struct pico_ip6 *prefix)
+{
+ struct pico_ip6 netmask64 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+ struct pico_6lowpan_info *info = (struct pico_6lowpan_info *)dev->eth;
+ struct pico_ipv6_link *link = NULL; /* Make sure to return NULL */
+ struct pico_ip6 newaddr;
+
+ memcpy(newaddr.addr, prefix->addr, PICO_SIZE_IP6);
+ memcpy(newaddr.addr + 8, info->addr_ext.addr, SIZE_6LOWPAN_EXT);
+ newaddr.addr[8] = newaddr.addr[8] ^ 0x02; /* Toggle U/L bit */
+
+ /* RFC6775: No Duplicate Address Detection (DAD) is performed if
+ * EUI-64-based IPv6 addresses are used (as these addresses are assumed
+ * to be globally unique). */
+ if ((link = pico_ipv6_link_add_no_dad(dev, newaddr, netmask64))) {
+ if (pico_ipv6_is_linklocal(newaddr.addr))
+ pico_6lp_nd_start_soliciting(link, NULL);
+ else
+ pico_6lp_nd_register(link);
+ }
+
+ return link;
+}
+
+static int pico_6lowpan_store_info(struct pico_device *dev, const uint8_t *mac)
+{
+ if ((dev->eth = PICO_ZALLOC(sizeof(struct pico_6lowpan_info)))) {
+ memcpy(dev->eth, mac, sizeof(struct pico_6lowpan_info));
+ return 0;
+ } else {
+ pico_err = PICO_ERR_ENOMEM;
+ return -1;
+ }
+}
+#endif
+
#ifdef PICO_SUPPORT_IPV6
static void device_init_ipv6_final(struct pico_device *dev, struct pico_ip6 *linklocal)
{
@@ -47,62 +89,78 @@ static void device_init_ipv6_final(struct pico_device *dev, struct pico_ip6 *lin
/* RFC 4861 $6.3.2 value between 0.5 and 1.5 times basetime */
dev->hostvars.reachabletime = ((5 + (pico_rand() % 10)) * PICO_ND_REACHABLE_TIME) / 10;
dev->hostvars.retranstime = PICO_ND_RETRANS_TIMER;
- pico_icmp6_router_solicitation(dev, linklocal);
+ pico_icmp6_router_solicitation(dev, linklocal, NULL);
dev->hostvars.hoplimit = PICO_IPV6_DEFAULT_HOP;
}
struct pico_ipv6_link *pico_ipv6_link_add_local(struct pico_device *dev, const struct pico_ip6 *prefix)
{
- struct pico_ip6 newaddr;
struct pico_ip6 netmask64 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
- struct pico_ipv6_link *link;
- memcpy(newaddr.addr, prefix->addr, PICO_SIZE_IP6);
- /* modified EUI-64 + invert universal/local bit */
- newaddr.addr[8] = (dev->eth->mac.addr[0] ^ 0x02);
- newaddr.addr[9] = dev->eth->mac.addr[1];
- newaddr.addr[10] = dev->eth->mac.addr[2];
- newaddr.addr[11] = 0xff;
- newaddr.addr[12] = 0xfe;
- newaddr.addr[13] = dev->eth->mac.addr[3];
- newaddr.addr[14] = dev->eth->mac.addr[4];
- newaddr.addr[15] = dev->eth->mac.addr[5];
- link = pico_ipv6_link_add(dev, newaddr, netmask64);
- if (link) {
- device_init_ipv6_final(dev, &newaddr);
+ struct pico_ipv6_link *link = NULL; /* Make sure to return NULL */
+ struct pico_ip6 newaddr;
+
+ if (0) {}
+#ifdef PICO_SUPPORT_6LOWPAN
+ else if (PICO_DEV_IS_6LOWPAN(dev)) {
+ link = pico_6lowpan_link_add(dev, prefix);
+ }
+#endif
+ else {
+ memcpy(newaddr.addr, prefix->addr, PICO_SIZE_IP6);
+ /* modified EUI-64 + invert universal/local bit */
+ newaddr.addr[8] = (dev->eth->mac.addr[0] ^ 0x02);
+ newaddr.addr[9] = dev->eth->mac.addr[1];
+ newaddr.addr[10] = dev->eth->mac.addr[2];
+ newaddr.addr[11] = 0xff;
+ newaddr.addr[12] = 0xfe;
+ newaddr.addr[13] = dev->eth->mac.addr[3];
+ newaddr.addr[14] = dev->eth->mac.addr[4];
+ newaddr.addr[15] = dev->eth->mac.addr[5];
+ if ((link = pico_ipv6_link_add(dev, newaddr, netmask64))) {
+ device_init_ipv6_final(dev, &newaddr);
+ }
}
return link;
}
#endif
-
-static int device_init_mac(struct pico_device *dev, uint8_t *mac)
+static int device_init_mac(struct pico_device *dev, const uint8_t *mac)
{
- #ifdef PICO_SUPPORT_IPV6
+#ifdef PICO_SUPPORT_IPV6
struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
- #endif
- dev->eth = PICO_ZALLOC(sizeof(struct pico_ethdev));
- if (dev->eth) {
- memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH);
- #ifdef PICO_SUPPORT_IPV6
- if (pico_ipv6_link_add_local(dev, &linklocal) == NULL) {
- PICO_FREE(dev->q_in);
- PICO_FREE(dev->q_out);
- PICO_FREE(dev->eth);
+#endif
+
+ if (0) {}
+#ifdef PICO_SUPPORT_6LOWPAN
+ else if (PICO_DEV_IS_6LOWPAN(dev)) {
+ if (pico_6lowpan_store_info(dev, mac))
+ return -1;
+ }
+#endif
+ else {
+ if ((dev->eth = PICO_ZALLOC(sizeof(struct pico_ethdev)))) {
+ memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH);
+ } else {
+ pico_err = PICO_ERR_ENOMEM;
return -1;
}
+ }
- #endif
- } else {
- pico_err = PICO_ERR_ENOMEM;
+#ifdef PICO_SUPPORT_IPV6
+ if (pico_ipv6_link_add_local(dev, &linklocal) == NULL) {
+ PICO_FREE(dev->q_in);
+ PICO_FREE(dev->q_out);
+ PICO_FREE(dev->eth);
return -1;
}
+#endif
return 0;
}
int pico_device_ipv6_random_ll(struct pico_device *dev)
{
- #ifdef PICO_SUPPORT_IPV6
+#ifdef PICO_SUPPORT_IPV6
struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
struct pico_ip6 netmask6 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
uint32_t len = (uint32_t)strlen(dev->name);
@@ -127,7 +185,7 @@ int pico_device_ipv6_random_ll(struct pico_device *dev)
}
}
- #endif
+#endif
return 0;
}
@@ -143,10 +201,17 @@ static int device_init_nomac(struct pico_device *dev)
return 0;
}
-extern int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac)
+#define DEBUG_IPV6(ip) { \
+ char ipstr[40] = { 0 }; \
+ pico_ipv6_to_string(ipstr, (ip).addr); \
+ dbg("IPv6 (%s)\n", ipstr); \
+ }
+
+int pico_device_init(struct pico_device *dev, const char *name, const uint8_t *mac)
{
uint32_t len = (uint32_t)strlen(name);
int ret = 0;
+
if(len > MAX_DEVICE_NAME)
len = MAX_DEVICE_NAME;
@@ -165,16 +230,33 @@ extern int pico_device_init(struct pico_device *dev, const char *name, uint8_t *
return -1;
}
- pico_tree_insert(&Device_tree, dev);
+ if (pico_tree_insert(&Device_tree, dev)) {
+ PICO_FREE(dev->q_in);
+ PICO_FREE(dev->q_out);
+ return -1;
+ }
if (!dev->mtu)
dev->mtu = PICO_DEVICE_DEFAULT_MTU;
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (PICO_DEV_IS_6LOWPAN(dev) && LL_MODE_ETHERNET == dev->mode)
+ return -1;
+#endif
+
if (mac) {
ret = device_init_mac(dev, mac);
} else {
- ret = device_init_nomac(dev);
+ if (!dev->mode) {
+ ret = device_init_nomac(dev);
+ }
+#ifdef PICO_SUPPORT_6LOWPAN
+ else {
+ /* RFC6775: Link Local to be formed based on EUI-64 as per RFC6775 */
+ dbg("Link local address to be formed based on EUI-64\n");
+ return -1;
+ }
+#endif
}
-
return ret;
}
@@ -192,7 +274,7 @@ void pico_device_destroy(struct pico_device *dev)
pico_queue_destroy(dev->q_in);
pico_queue_destroy(dev->q_out);
- if (dev->eth)
+ if (!dev->mode && dev->eth)
PICO_FREE(dev->eth);
#ifdef PICO_SUPPORT_IPV4
@@ -202,7 +284,7 @@ void pico_device_destroy(struct pico_device *dev)
pico_ipv6_cleanup_links(dev);
#endif
pico_tree_delete(&Device_tree, dev);
-
+
if (dev->destroy)
dev->destroy(dev);
@@ -240,14 +322,7 @@ static int devloop_in(struct pico_device *dev, int loop_score)
/* Receive */
f = pico_dequeue(dev->q_in);
if (f) {
- if (dev->eth) {
- f->datalink_hdr = f->buffer;
- (void)pico_ethernet_receive(f);
- } else {
- f->net_hdr = f->buffer;
- pico_network_receive(f);
- }
-
+ pico_datalink_receive(f);
loop_score--;
}
}
@@ -256,14 +331,12 @@ static int devloop_in(struct pico_device *dev, int loop_score)
static int devloop_sendto_dev(struct pico_device *dev, struct pico_frame *f)
{
-
- if (dev->eth) {
- /* Ethernet: pass management of the frame to the pico_ethernet_send() rdv function */
- return pico_ethernet_send(f);
- } else {
- /* non-ethernet: no post-processing needed */
- return (dev->send(dev, f->start, (int)f->len) <= 0); /* Return 0 upon success, which is dev->send() > 0 */
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (PICO_DEV_IS_6LOWPAN(dev)) {
+ return (pico_6lowpan_ll_sendto_dev(dev, f) <= 0);
}
+#endif
+ return (dev->send(dev, f->start, (int)f->len) <= 0);
}
static int devloop_out(struct pico_device *dev, int loop_score)
@@ -286,6 +359,7 @@ static int devloop_out(struct pico_device *dev, int loop_score)
break; /* Don't discard */
}
+
return loop_score;
}
diff --git a/ext/picotcp/stack/pico_frame.c b/ext/picotcp/stack/pico_frame.c
index ef5f8d7..7aad7f3 100644
--- a/ext/picotcp/stack/pico_frame.c
+++ b/ext/picotcp/stack/pico_frame.c
@@ -1,17 +1,17 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Authors: Daniele Lacamera
*********************************************************************/
-
#include "pico_config.h"
#include "pico_frame.h"
#include "pico_protocol.h"
#include "pico_stack.h"
+#include "pico_socket.h"
#ifdef PICO_SUPPORT_DEBUG_MEMORY
static int n_frames_allocated;
@@ -64,7 +64,6 @@ struct pico_frame *pico_frame_copy(struct pico_frame *f)
return new;
}
-
static struct pico_frame *pico_frame_do_alloc(uint32_t size, int zerocopy, int ext_buffer)
{
struct pico_frame *p = PICO_ZALLOC(sizeof(struct pico_frame));
@@ -85,13 +84,13 @@ static struct pico_frame *pico_frame_do_alloc(uint32_t size, int zerocopy, int e
frame_buffer_size += (uint32_t)sizeof(uint32_t) - align;
}
- p->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t));
+ p->buffer = PICO_ZALLOC((size_t)frame_buffer_size + sizeof(uint32_t));
if (!p->buffer) {
PICO_FREE(p);
return NULL;
}
- p->usage_count = (uint32_t *)((void *)(((uint8_t*)p->buffer) + frame_buffer_size));
+ p->usage_count = (uint32_t *)(((uint8_t*)p->buffer) + frame_buffer_size);
} else {
p->buffer = NULL;
p->flags |= PICO_FRAME_FLAG_EXT_USAGE_COUNTER;
@@ -102,13 +101,17 @@ static struct pico_frame *pico_frame_do_alloc(uint32_t size, int zerocopy, int e
}
}
-
p->buffer_len = size;
/* By default, frame content is the full buffer. */
p->start = p->buffer;
p->len = p->buffer_len;
*p->usage_count = 1;
+ p->net_hdr = p->buffer;
+ p->datalink_hdr = p->buffer;
+ p->transport_hdr = p->buffer;
+ p->app_hdr = p->buffer;
+ p->payload = p->buffer;
if (ext_buffer)
p->flags |= PICO_FRAME_FLAG_EXT_BUFFER;
@@ -125,17 +128,16 @@ struct pico_frame *pico_frame_alloc(uint32_t size)
return pico_frame_do_alloc(size, 0, 0);
}
-int pico_frame_grow(struct pico_frame *f, uint32_t size)
+static uint8_t *
+pico_frame_new_buffer(struct pico_frame *f, uint32_t size, uint32_t *oldsize)
{
uint8_t *oldbuf;
uint32_t usage_count, *p_old_usage;
uint32_t frame_buffer_size;
- uint32_t oldsize;
unsigned int align;
- int addr_diff = 0;
if (!f || (size < f->buffer_len)) {
- return -1;
+ return NULL;
}
align = size % sizeof(uint32_t);
@@ -145,22 +147,28 @@ int pico_frame_grow(struct pico_frame *f, uint32_t size)
}
oldbuf = f->buffer;
- oldsize = f->buffer_len;
+ *oldsize = f->buffer_len;
usage_count = *(f->usage_count);
p_old_usage = f->usage_count;
- f->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t));
+ f->buffer = PICO_ZALLOC((size_t)frame_buffer_size + sizeof(uint32_t));
if (!f->buffer) {
f->buffer = oldbuf;
- return -1;
+ return NULL;
}
- f->usage_count = (uint32_t *)((void *)(((uint8_t*)f->buffer) + frame_buffer_size));
+ f->usage_count = (uint32_t *)(((uint8_t*)f->buffer) + frame_buffer_size);
*f->usage_count = usage_count;
f->buffer_len = size;
- memcpy(f->buffer, oldbuf, oldsize);
- /* Update hdr fields to new buffer*/
- addr_diff = (int)(f->buffer - oldbuf);
+ if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER)
+ PICO_FREE(p_old_usage);
+ /* Now, the frame is not zerocopy anymore, and the usage counter has been moved within it */
+ return oldbuf;
+}
+
+static int
+pico_frame_update_pointers(struct pico_frame *f, ptrdiff_t addr_diff, uint8_t *oldbuf)
+{
f->net_hdr += addr_diff;
f->datalink_hdr += addr_diff;
f->transport_hdr += addr_diff;
@@ -168,19 +176,45 @@ int pico_frame_grow(struct pico_frame *f, uint32_t size)
f->start += addr_diff;
f->payload += addr_diff;
- if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER)
- PICO_FREE(p_old_usage);
-
if (!(f->flags & PICO_FRAME_FLAG_EXT_BUFFER))
PICO_FREE(oldbuf);
else if (f->notify_free)
f->notify_free(oldbuf);
f->flags = 0;
- /* Now, the frame is not zerocopy anymore, and the usage counter has been moved within it */
return 0;
}
+int pico_frame_grow_head(struct pico_frame *f, uint32_t size)
+{
+ ptrdiff_t addr_diff = 0;
+ uint32_t oldsize = 0;
+ uint8_t *oldbuf = pico_frame_new_buffer(f, size, &oldsize);
+ if (!oldbuf)
+ return -1;
+
+ /* Put old buffer at the end of new buffer */
+ memcpy(f->buffer + f->buffer_len - oldsize, oldbuf, (size_t)oldsize);
+ addr_diff = (ptrdiff_t)(f->buffer + f->buffer_len - oldsize - oldbuf);
+
+ return pico_frame_update_pointers(f, addr_diff, oldbuf);
+}
+
+int pico_frame_grow(struct pico_frame *f, uint32_t size)
+{
+ ptrdiff_t addr_diff = 0;
+ uint32_t oldsize = 0;
+ uint8_t *oldbuf = pico_frame_new_buffer(f, size, &oldsize);
+ if (!oldbuf)
+ return -1;
+
+ /* Just put old buffer at the beginning of new buffer */
+ memcpy(f->buffer, oldbuf, (size_t)oldsize);
+ addr_diff = (ptrdiff_t)(f->buffer - oldbuf);
+
+ return pico_frame_update_pointers(f, addr_diff, oldbuf);
+}
+
struct pico_frame *pico_frame_alloc_skeleton(uint32_t size, int ext_buffer)
{
return pico_frame_do_alloc(size, 1, ext_buffer);
@@ -199,7 +233,7 @@ int pico_frame_skeleton_set_buffer(struct pico_frame *f, void *buf)
struct pico_frame *pico_frame_deepcopy(struct pico_frame *f)
{
struct pico_frame *new = pico_frame_alloc(f->buffer_len);
- int addr_diff;
+ ptrdiff_t addr_diff;
unsigned char *buf;
uint32_t *uc;
if (!new)
@@ -217,7 +251,7 @@ struct pico_frame *pico_frame_deepcopy(struct pico_frame *f)
new->usage_count = uc;
/* Update in-buffer pointers with offset */
- addr_diff = (int)(new->buffer - f->buffer);
+ addr_diff = (ptrdiff_t)(new->buffer - f->buffer);
new->datalink_hdr += addr_diff;
new->net_hdr += addr_diff;
new->transport_hdr += addr_diff;
@@ -225,6 +259,15 @@ struct pico_frame *pico_frame_deepcopy(struct pico_frame *f)
new->start += addr_diff;
new->payload += addr_diff;
+ if (f->info) {
+ new->info = PICO_ZALLOC(sizeof(struct pico_remote_endpoint));
+ if (!new->info) {
+ pico_frame_discard(new);
+ return NULL;
+ }
+ memcpy(new->info, f->info, sizeof(struct pico_remote_endpoint));
+ }
+
#ifdef PICO_SUPPORT_DEBUG_MEMORY
dbg("Deep-Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count);
#endif
@@ -247,7 +290,7 @@ static inline uint32_t pico_checksum_adder(uint32_t sum, void *data, uint32_t le
#endif
}
- stop = (uint16_t *)((void *)(((uint8_t *)data) + len));
+ stop = (uint16_t *)(((uint8_t *)data) + len);
while (buf < stop) {
sum += *buf++;
diff --git a/ext/picotcp/stack/pico_md5.c b/ext/picotcp/stack/pico_md5.c
index d612bce..2c235be 100644
--- a/ext/picotcp/stack/pico_md5.c
+++ b/ext/picotcp/stack/pico_md5.c
@@ -1,6 +1,6 @@
/*********************************************************************
- * PicoTCP. Copyright (c) 2015 Altran Intelligent Systems. Some rights reserved.
- * See LICENSE and COPYING for usage.
+ * PicoTCP. Copyright (c) 2015-2017 Altran Intelligent Systems. Some rights reserved.
+ * See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*
* Authors: Daniele Lacamera
* *********************************************************************/
diff --git a/ext/picotcp/stack/pico_protocol.c b/ext/picotcp/stack/pico_protocol.c
index b8afec2..a4b16cf 100644
--- a/ext/picotcp/stack/pico_protocol.c
+++ b/ext/picotcp/stack/pico_protocol.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -10,6 +10,7 @@
#include "pico_protocol.h"
#include "pico_tree.h"
+#include "../../../include/Debug.hpp"
struct pico_proto_rr
{
@@ -30,10 +31,10 @@ static int pico_proto_cmp(void *ka, void *kb)
return 0;
}
-PICO_TREE_DECLARE(Datalink_proto_tree, pico_proto_cmp);
-PICO_TREE_DECLARE(Network_proto_tree, pico_proto_cmp);
-PICO_TREE_DECLARE(Transport_proto_tree, pico_proto_cmp);
-PICO_TREE_DECLARE(Socket_proto_tree, pico_proto_cmp);
+static PICO_TREE_DECLARE(Datalink_proto_tree, pico_proto_cmp);
+static PICO_TREE_DECLARE(Network_proto_tree, pico_proto_cmp);
+static PICO_TREE_DECLARE(Transport_proto_tree, pico_proto_cmp);
+static PICO_TREE_DECLARE(Socket_proto_tree, pico_proto_cmp);
/* Static variables to keep track of the round robin loop */
static struct pico_proto_rr proto_rr_datalink = {
@@ -186,29 +187,41 @@ static void proto_layer_rr_reset(struct pico_proto_rr *rr)
void pico_protocol_init(struct pico_protocol *p)
{
+ struct pico_tree *tree = NULL;
+ struct pico_proto_rr *proto = NULL;
+
if (!p)
return;
p->hash = pico_hash(p->name, (uint32_t)strlen(p->name));
switch (p->layer) {
- case PICO_LAYER_DATALINK:
- pico_tree_insert(&Datalink_proto_tree, p);
- proto_layer_rr_reset(&proto_rr_datalink);
- break;
- case PICO_LAYER_NETWORK:
- pico_tree_insert(&Network_proto_tree, p);
- proto_layer_rr_reset(&proto_rr_network);
- break;
- case PICO_LAYER_TRANSPORT:
- pico_tree_insert(&Transport_proto_tree, p);
- proto_layer_rr_reset(&proto_rr_transport);
- break;
- case PICO_LAYER_SOCKET:
- pico_tree_insert(&Socket_proto_tree, p);
- proto_layer_rr_reset(&proto_rr_socket);
- break;
+ case PICO_LAYER_DATALINK:
+ tree = &Datalink_proto_tree;
+ proto = &proto_rr_datalink;
+ break;
+ case PICO_LAYER_NETWORK:
+ tree = &Network_proto_tree;
+ proto = &proto_rr_network;
+ break;
+ case PICO_LAYER_TRANSPORT:
+ tree = &Transport_proto_tree;
+ proto = &proto_rr_transport;
+ break;
+ case PICO_LAYER_SOCKET:
+ tree = &Socket_proto_tree;
+ proto = &proto_rr_socket;
+ break;
+ default:
+ DEBUG_EXTRA("Unknown protocol: %s (layer: %d)", p->name, p->layer);
+ return;
}
- //dbg("Protocol %s registered (layer: %d).\n", p->name, p->layer);
+ if (pico_tree_insert(tree, p)) {
+ DEBUG_EXTRA("Failed to insert protocol %s", p->name);
+ return;
+ }
+
+ proto_layer_rr_reset(proto);
+ DEBUG_EXTRA("Protocol %s registered (layer: %d).", p->name, p->layer);
}
diff --git a/ext/picotcp/stack/pico_socket.c b/ext/picotcp/stack/pico_socket.c
index f36cb80..82b5062 100644
--- a/ext/picotcp/stack/pico_socket.c
+++ b/ext/picotcp/stack/pico_socket.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
@@ -23,6 +23,8 @@
#include "pico_socket_tcp.h"
#include "pico_socket_udp.h"
+#include "../../../include/Debug.hpp"
+
#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6)
#if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP)
@@ -35,17 +37,30 @@
static void *Mutex = NULL;
#endif
+/* Mockables */
+#if defined UNIT_TEST
+# define MOCKABLE __attribute__((weak))
+#else
+# define MOCKABLE
+#endif
#define PROTO(s) ((s)->proto->proto_number)
#define PICO_SOCKET_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */
-# define frag_dbg(...) do {} while(0)
+#ifdef PICO_SUPPORT_IPV4FRAG
+#ifdef DEBUG_FRAG
+#define frag_dbg dbg
+#else
+#define frag_dbg(...) do {} while(0)
+#endif
+
+#endif
static struct pico_sockport *sp_udp = NULL, *sp_tcp = NULL;
-struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len);
+struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, struct pico_device *dev, uint16_t len);
static int socket_cmp_family(struct pico_socket *a, struct pico_socket *b)
{
@@ -123,7 +138,7 @@ static int socket_cmp_addresses(struct pico_socket *a, struct pico_socket *b)
if (ret == 0)
ret = socket_cmp_remotehost(a, b);
- return 0;
+ return ret;
}
static int socket_cmp(void *ka, void *kb)
@@ -160,25 +175,21 @@ static int sockport_cmp(void *ka, void *kb)
return 0;
}
-PICO_TREE_DECLARE(UDPTable, sockport_cmp);
-PICO_TREE_DECLARE(TCPTable, sockport_cmp);
+static PICO_TREE_DECLARE(UDPTable, sockport_cmp);
+static PICO_TREE_DECLARE(TCPTable, sockport_cmp);
struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port)
{
struct pico_sockport test = INIT_SOCKPORT;
test.number = port;
- if (proto == PICO_PROTO_UDP){
+ if (proto == PICO_PROTO_UDP)
return pico_tree_findKey(&UDPTable, &test);
- }
- else if (proto == PICO_PROTO_TCP){
+ else if (proto == PICO_PROTO_TCP)
return pico_tree_findKey(&TCPTable, &test);
- }
- else
- { return NULL;
- }
+ else return NULL;
}
#ifdef PICO_SUPPORT_IPV4
@@ -344,6 +355,7 @@ static int pico_check_socket(struct pico_socket *s)
return 0;
}
}
+
return -1;
}
@@ -365,13 +377,21 @@ struct pico_socket *pico_sockets_find(uint16_t local, uint16_t remote)
}
}
}
+
return sock;
}
int8_t pico_socket_add(struct pico_socket *s)
{
- struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port);
+ struct pico_sockport *sp;
+ if (PROTO(s) != PICO_PROTO_UDP && PROTO(s) != PICO_PROTO_TCP)
+ {
+ pico_err = PICO_ERR_EINVAL;
+ return -1;
+ }
+
+ sp = pico_get_sockport(PROTO(s), s->local_port);
PICOTCP_MUTEX_LOCK(Mutex);
if (!sp) {
/* dbg("Creating sockport..%04x\n", s->local_port); / * In comment due to spam during test * / */
@@ -390,21 +410,32 @@ int8_t pico_socket_add(struct pico_socket *s)
if (PROTO(s) == PICO_PROTO_UDP)
{
- pico_tree_insert(&UDPTable, sp);
+ if (pico_tree_insert(&UDPTable, sp)) {
+ PICO_FREE(sp);
+ PICOTCP_MUTEX_UNLOCK(Mutex);
+ return -1;
+ }
+
}
else if (PROTO(s) == PICO_PROTO_TCP)
{
- pico_tree_insert(&TCPTable, sp);
+ if (pico_tree_insert(&TCPTable, sp)) {
+ PICO_FREE(sp);
+ PICOTCP_MUTEX_UNLOCK(Mutex);
+ return -1;
+ }
}
}
- pico_tree_insert(&sp->socks, s);
+ if (pico_tree_insert(&sp->socks, s)) {
+ PICOTCP_MUTEX_UNLOCK(Mutex);
+ return -1;
+ }
s->state |= PICO_SOCKET_STATE_BOUND;
PICOTCP_MUTEX_UNLOCK(Mutex);
#ifdef DEBUG_SOCKET_TREE
{
struct pico_tree_node *index;
- /* RB_FOREACH(s, socket_tree, &sp->socks) { */
pico_tree_foreach(index, &sp->socks){
s = index->keyValue;
dbg(">>>> List Socket lc=%hu rm=%hu\n", short_be(s->local_port), short_be(s->remote_port));
@@ -482,10 +513,17 @@ int8_t pico_socket_del(struct pico_socket *s)
PICOTCP_MUTEX_LOCK(Mutex);
pico_tree_delete(&sp->socks, s);
pico_socket_check_empty_sockport(s, sp);
+#ifdef PICO_SUPPORT_MCAST
pico_multicast_delete(s);
+#endif
pico_socket_tcp_delete(s);
s->state = PICO_SOCKET_STATE_CLOSED;
- pico_timer_add((pico_time)10, socket_garbage_collect, s);
+ if (!pico_timer_add((pico_time)10, socket_garbage_collect, s)) {
+ dbg("SOCKET: Failed to start garbage collect timer, doing garbage collection now\n");
+ PICOTCP_MUTEX_UNLOCK(Mutex);
+ socket_garbage_collect((pico_time)0, s);
+ return -1;
+ }
PICOTCP_MUTEX_UNLOCK(Mutex);
return 0;
}
@@ -548,7 +586,7 @@ static int pico_socket_deliver(struct pico_protocol *p, struct pico_frame *f, ui
sp = pico_get_sockport(p->proto_number, localport);
if (!sp) {
- //dbg("No such port %d\n", short_be(localport));
+ DEBUG_EXTRA("No such port %d", short_be(localport));
return -1;
}
@@ -597,7 +635,7 @@ static struct pico_socket *pico_socket_transport_open(uint16_t proto, uint16_t f
}
-extern struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *))
+extern struct pico_socket *MOCKABLE pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *))
{
struct pico_socket *s = NULL;
@@ -688,7 +726,6 @@ static int pico_socket_transport_read(struct pico_socket *s, void *buf, int len)
return pico_socket_tcp_read(s, buf, (uint32_t)len);
else return 0;
}
-#include
int pico_socket_read(struct pico_socket *s, void *buf, int len)
{
@@ -1036,11 +1073,32 @@ static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const in
struct pico_remote_endpoint *ep, struct pico_msginfo *msginfo)
{
struct pico_frame *f;
+ struct pico_device *dev = NULL;
uint16_t hdr_offset = (uint16_t)pico_socket_sendto_transport_offset(s);
int ret = 0;
(void)src;
-
- f = pico_socket_frame_alloc(s, (uint16_t)(len + hdr_offset));
+
+ if (msginfo) {
+ dev = msginfo->dev;
+ }
+#ifdef PICO_SUPPORT_IPV6
+ else if (IS_SOCK_IPV6(s) && ep && pico_ipv6_is_multicast(&ep->remote_addr.ip6.addr[0])) {
+ dev = pico_ipv6_link_find(src);
+ }
+#endif
+ else if (IS_SOCK_IPV6(s) && ep) {
+ dev = pico_ipv6_source_dev_find(&ep->remote_addr.ip6);
+ } else if (IS_SOCK_IPV4(s) && ep) {
+ dev = pico_ipv4_source_dev_find(&ep->remote_addr.ip4);
+ } else {
+ dev = get_sock_dev(s);
+ }
+
+ if (!dev) {
+ return -1;
+ }
+
+ f = pico_socket_frame_alloc(s, dev, (uint16_t)(len + hdr_offset));
if (!f) {
pico_err = PICO_ERR_ENOMEM;
return -1;
@@ -1062,16 +1120,8 @@ static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const in
if (msginfo) {
f->send_ttl = (uint8_t)msginfo->ttl;
f->send_tos = (uint8_t)msginfo->tos;
- f->dev = msginfo->dev;
}
-#ifdef PICO_SUPPORT_IPV6
- if(IS_SOCK_IPV6(s) && ep && pico_ipv6_is_multicast(&ep->remote_addr.ip6.addr[0])) {
- f->dev = pico_ipv6_link_find(src);
- if(!f->dev) {
- return -1;
- }
- }
-#endif
+
memcpy(f->payload, (const uint8_t *)buf, f->payload_len);
/* dbg("Pushing segment, hdr len: %d, payload_len: %d\n", header_offset, f->payload_len); */
ret = pico_socket_final_xmit(s, f);
@@ -1116,6 +1166,12 @@ static int pico_socket_xmit_fragments(struct pico_socket *s, const void *buf, co
int retval = 0;
struct pico_frame *f = NULL;
+ if (space < 0) {
+ pico_err = PICO_ERR_EPROTONOSUPPORT;
+ pico_endpoint_free(ep);
+ return -1;
+ }
+
if (space > len) {
retval = pico_socket_xmit_one(s, buf, len, src, ep, msginfo);
pico_endpoint_free(ep);
@@ -1141,7 +1197,7 @@ static int pico_socket_xmit_fragments(struct pico_socket *s, const void *buf, co
if (space > len - total_payload_written) /* update space for last fragment */
space = len - total_payload_written;
- f = pico_socket_frame_alloc(s, (uint16_t)(space + hdr_offset));
+ f = pico_socket_frame_alloc(s, get_sock_dev(s), (uint16_t)(space + hdr_offset));
if (!f) {
pico_err = PICO_ERR_ENOMEM;
pico_endpoint_free(ep);
@@ -1196,7 +1252,7 @@ static int pico_socket_xmit_fragments(struct pico_socket *s, const void *buf, co
#endif
}
-static void get_sock_dev(struct pico_socket *s)
+struct pico_device *get_sock_dev(struct pico_socket *s)
{
if (0) {}
@@ -1209,6 +1265,7 @@ static void get_sock_dev(struct pico_socket *s)
s->dev = pico_ipv4_source_dev_find(&s->remote_addr.ip4);
#endif
+ return s->dev;
}
@@ -1423,8 +1480,8 @@ int pico_socket_recvfrom_extended(struct pico_socket *s, void *buf, int len, voi
return 0;
}
-extern int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig,
- uint16_t *remote_port)
+extern int MOCKABLE pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig,
+ uint16_t *remote_port)
{
return pico_socket_recvfrom_extended(s, buf, len, orig, remote_port, NULL);
@@ -1499,7 +1556,7 @@ int pico_socket_getpeername(struct pico_socket *s, void *remote_addr, uint16_t *
}
-int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port)
+int MOCKABLE pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port)
{
if (!s || !local_addr || !port) {
pico_err = PICO_ERR_EINVAL;
@@ -1684,7 +1741,7 @@ extern int pico_socket_listen(struct pico_socket *s, int backlog)
return 0;
}
-struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port)
+extern struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port)
{
if (!s || !orig || !port) {
pico_err = PICO_ERR_EINVAL;
@@ -1736,7 +1793,7 @@ struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16
#else
-extern int pico_socket_listen(struct pico_socket *s, int backlog)
+int pico_socket_listen(struct pico_socket *s, int backlog)
{
IGNORE_PARAMETER(s);
IGNORE_PARAMETER(backlog);
@@ -1744,7 +1801,7 @@ extern int pico_socket_listen(struct pico_socket *s, int backlog)
return -1;
}
-extern struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *local_port)
+struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *local_port)
{
IGNORE_PARAMETER(s);
IGNORE_PARAMETER(orig);
@@ -1756,7 +1813,7 @@ extern struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig,
#endif
-int pico_socket_setoption(struct pico_socket *s, int option, void *value)
+int MOCKABLE pico_socket_setoption(struct pico_socket *s, int option, void *value)
{
if (s == NULL) {
@@ -1960,6 +2017,7 @@ static int check_socket_sanity(struct pico_socket *s)
if((PICO_TIME_MS() - s->timestamp) >= PICO_SOCKET_BOUND_TIMEOUT)
return -1;
}
+
return 0;
}
#endif
@@ -2113,19 +2171,19 @@ int pico_count_sockets(uint8_t proto)
}
-struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len)
+struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, struct pico_device *dev, uint16_t len)
{
struct pico_frame *f = NULL;
#ifdef PICO_SUPPORT_IPV6
if (is_sock_ipv6(s))
- f = pico_proto_ipv6.alloc(&pico_proto_ipv6, len);
+ f = pico_proto_ipv6.alloc(&pico_proto_ipv6, dev, len);
#endif
#ifdef PICO_SUPPORT_IPV4
if (is_sock_ipv4(s))
- f = pico_proto_ipv4.alloc(&pico_proto_ipv4, len);
+ f = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, len);
#endif
if (!f) {
diff --git a/ext/picotcp/stack/pico_socket_multicast.c b/ext/picotcp/stack/pico_socket_multicast.c
index 132881c..8e69e45 100644
--- a/ext/picotcp/stack/pico_socket_multicast.c
+++ b/ext/picotcp/stack/pico_socket_multicast.c
@@ -8,33 +8,37 @@
#include "pico_udp.h"
#ifdef PICO_SUPPORT_MCAST
-# define so_mcast_dbg(...) do { }while(0) /* ip_mcast_dbg in pico_ipv4.c */
-/* #define so_mcast_dbg dbg */
+
+#ifdef DEBUG_MCAST
+#define so_mcast_dbg dbg
+#else
+#define so_mcast_dbg(...) do { } while(0)
+#endif
/* socket
-* |
-* MCASTListen
-* | | |
-* ------------ | ------------
-* | | |
-* MCASTSources MCASTSources MCASTSources
-* | | | | | | | | | | | |
-* S S S S S S S S S S S S
-*
-* MCASTListen: RBTree(mcast_link, mcast_group)
-* MCASTSources: RBTree(source)
-*/
+ * |
+ * MCASTListen
+ * | | |
+ * ------------ | ------------
+ * | | |
+ * MCASTSources MCASTSources MCASTSources
+ * | | | | | | | | | | | |
+ * S S S S S S S S S S S S
+ *
+ * MCASTListen: RBTree(mcast_link, mcast_group)
+ * MCASTSources: RBTree(source)
+ */
struct pico_mcast_listen
{
- uint8_t filter_mode;
+ int8_t filter_mode;
union pico_address mcast_link;
union pico_address mcast_group;
struct pico_tree MCASTSources;
struct pico_tree MCASTSources_ipv6;
uint16_t proto;
};
-//Parameters
-struct pico_mcast
+/* Parameters */
+struct pico_mcast
{
struct pico_socket *s;
struct pico_ip_mreq *mreq;
@@ -71,6 +75,7 @@ static int mcast_listen_grp_cmp_ipv6(struct pico_mcast_listen *a, struct pico_mc
int tmp = memcmp(&a->mcast_group.ip6, &b->mcast_group.ip6, sizeof(struct pico_ip6));
if(!tmp)
return mcast_listen_link_cmp(a, b);
+
return tmp;
}
#endif
@@ -130,7 +135,7 @@ static int mcast_socket_cmp(void *ka, void *kb)
}
/* gather all multicast sockets to hasten filter aggregation */
-PICO_TREE_DECLARE(MCASTSockets, mcast_socket_cmp);
+static PICO_TREE_DECLARE(MCASTSockets, mcast_socket_cmp);
static int mcast_filter_cmp(void *ka, void *kb)
{
@@ -144,7 +149,7 @@ static int mcast_filter_cmp(void *ka, void *kb)
return 0;
}
/* gather sources to be filtered */
-PICO_TREE_DECLARE(MCASTFilter, mcast_filter_cmp);
+static PICO_TREE_DECLARE(MCASTFilter, mcast_filter_cmp);
static int mcast_filter_cmp_ipv6(void *ka, void *kb)
{
@@ -152,35 +157,41 @@ static int mcast_filter_cmp_ipv6(void *ka, void *kb)
return memcmp(&a->ip6, &b->ip6, sizeof(struct pico_ip6));
}
/* gather sources to be filtered */
-PICO_TREE_DECLARE(MCASTFilter_ipv6, mcast_filter_cmp_ipv6);
+static PICO_TREE_DECLARE(MCASTFilter_ipv6, mcast_filter_cmp_ipv6);
-inline static struct pico_tree *mcast_get_src_tree(struct pico_socket *s,struct pico_mcast *mcast) {
+inline static struct pico_tree *mcast_get_src_tree(struct pico_socket *s, struct pico_mcast *mcast)
+{
if( IS_SOCK_IPV4(s)) {
mcast->listen->MCASTSources.compare = mcast_sources_cmp;
return &mcast->listen->MCASTSources;
- }
-#ifdef PICO_SUPPORT_IPV6
- else if( IS_SOCK_IPV6(s) ) {
+ }
+
+#ifdef PICO_SUPPORT_IPV6
+ else if( IS_SOCK_IPV6(s)) {
mcast->listen->MCASTSources_ipv6.compare = mcast_sources_cmp_ipv6;
return &mcast->listen->MCASTSources_ipv6;
}
#endif
return NULL;
}
-inline static struct pico_tree *mcast_get_listen_tree(struct pico_socket *s) {
- if( IS_SOCK_IPV4(s))
+inline static struct pico_tree *mcast_get_listen_tree(struct pico_socket *s)
+{
+ if( IS_SOCK_IPV4(s))
return s->MCASTListen;
+
#ifdef PICO_SUPPORT_IPV6
- else if( IS_SOCK_IPV6(s) )
+ else if( IS_SOCK_IPV6(s))
return s->MCASTListen_ipv6;
#endif
return NULL;
}
-inline static void mcast_set_listen_tree_p_null(struct pico_socket *s) {
- if( IS_SOCK_IPV4(s))
+inline static void mcast_set_listen_tree_p_null(struct pico_socket *s)
+{
+ if( IS_SOCK_IPV4(s))
s->MCASTListen = NULL;
+
#ifdef PICO_SUPPORT_IPV6
- else if( IS_SOCK_IPV6(s) )
+ else if( IS_SOCK_IPV6(s))
s->MCASTListen_ipv6 = NULL;
#endif
}
@@ -192,26 +203,30 @@ static struct pico_mcast_listen *listen_find(struct pico_socket *s, union pico_a
ltest.mcast_link = *lnk;
ltest.mcast_group = *grp;
- if(IS_SOCK_IPV4(s))
+ if(IS_SOCK_IPV4(s))
return pico_tree_findKey(s->MCASTListen, <est);
+
#ifdef PICO_SUPPORT_IPV6
- else if(IS_SOCK_IPV6(s) ) {
+ else if(IS_SOCK_IPV6(s)) {
ltest.proto = PICO_PROTO_IPV6;
return pico_tree_findKey(s->MCASTListen_ipv6, <est);
}
#endif
return NULL;
}
-static union pico_address *pico_mcast_get_link_address(struct pico_socket *s, union pico_link *mcast_link) {
- if( IS_SOCK_IPV4(s) )
+static union pico_address *pico_mcast_get_link_address(struct pico_socket *s, union pico_link *mcast_link)
+{
+ if( IS_SOCK_IPV4(s))
return (union pico_address *) &mcast_link->ipv4.address;
+
#ifdef PICO_SUPPORT_IPV6
- if( IS_SOCK_IPV6(s))
+ if( IS_SOCK_IPV6(s))
return (union pico_address *) &mcast_link->ipv6.address;
+
#endif
return NULL;
}
-static uint8_t pico_mcast_filter_excl_excl(struct pico_mcast_listen *listen)
+static int8_t pico_mcast_filter_excl_excl(struct pico_mcast_listen *listen)
{
/* filter = intersection of EXCLUDEs */
/* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */
@@ -226,6 +241,7 @@ static uint8_t pico_mcast_filter_excl_excl(struct pico_mcast_listen *listen)
pico_tree_delete(&MCASTFilter, index->keyValue);
}
}
+
#ifdef PICO_SUPPORT_IPV6
if(!pico_tree_empty(&MCASTFilter_ipv6)) {
pico_tree_foreach_safe(index, &MCASTFilter_ipv6, _tmp)
@@ -235,11 +251,12 @@ static uint8_t pico_mcast_filter_excl_excl(struct pico_mcast_listen *listen)
pico_tree_delete(&MCASTFilter_ipv6, index->keyValue);
}
}
+
#endif
return PICO_IP_MULTICAST_EXCLUDE;
}
-static uint8_t pico_mcast_filter_excl_incl(struct pico_mcast_listen *listen)
+static int8_t pico_mcast_filter_excl_incl(struct pico_mcast_listen *listen)
{
/* filter = EXCLUDE - INCLUDE */
/* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */
@@ -254,6 +271,7 @@ static uint8_t pico_mcast_filter_excl_incl(struct pico_mcast_listen *listen)
pico_tree_delete(&MCASTFilter, source);
}
}
+
#ifdef PICO_SUPPORT_IPV6
if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
pico_tree_foreach_safe(index, &listen->MCASTSources_ipv6, _tmp)
@@ -263,17 +281,18 @@ static uint8_t pico_mcast_filter_excl_incl(struct pico_mcast_listen *listen)
pico_tree_delete(&MCASTFilter_ipv6, source);
}
}
+
#endif
return PICO_IP_MULTICAST_EXCLUDE;
}
-static uint8_t pico_mcast_filter_incl_excl(struct pico_mcast_listen *listen)
+static int8_t pico_mcast_filter_incl_excl(struct pico_mcast_listen *listen)
{
/* filter = EXCLUDE - INCLUDE */
/* delete from the interface INCLUDE filter any source NOT in the socket EXCLUDE filter */
struct pico_tree_node *index = NULL, *_tmp = NULL;
union pico_address *source = NULL;
- if(!pico_tree_empty(&listen->MCASTSources)) {
+ if(!pico_tree_empty(&listen->MCASTSources)) {
pico_tree_foreach_safe(index, &MCASTFilter, _tmp)
{
source = pico_tree_findKey(&listen->MCASTSources, index->keyValue);
@@ -281,15 +300,17 @@ static uint8_t pico_mcast_filter_incl_excl(struct pico_mcast_listen *listen)
pico_tree_delete(&MCASTFilter, index->keyValue);
}
}
+
#ifdef PICO_SUPPORT_IPV6
- if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
+ if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
pico_tree_foreach_safe(index, &MCASTFilter_ipv6, _tmp)
{
source = pico_tree_findKey(&listen->MCASTSources_ipv6, index->keyValue);
if (!source)
- pico_tree_delete(&MCASTFilter_ipv6, index->keyValue);
+ pico_tree_delete(&MCASTFilter_ipv6, index->keyValue);
}
}
+
#endif
/* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */
@@ -298,73 +319,87 @@ static uint8_t pico_mcast_filter_incl_excl(struct pico_mcast_listen *listen)
pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
{
source = pico_tree_insert(&MCASTFilter, index->keyValue);
- if (source)
- pico_tree_delete(&MCASTFilter, source);
+ if (source) {
+ if ((void *)source == (void *)&LEAF)
+ return -1;
+ else
+ pico_tree_delete(&MCASTFilter, source);
+ }
}
}
+
#ifdef PICO_SUPPORT_IPV6
if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
pico_tree_foreach_safe(index, &listen->MCASTSources_ipv6, _tmp)
{
source = pico_tree_insert(&MCASTFilter_ipv6, index->keyValue);
- if (source)
- pico_tree_delete(&MCASTFilter_ipv6, source);
+ if (source) {
+ if ((void *)source == (void *)&LEAF)
+ return -1;
+ else
+ pico_tree_delete(&MCASTFilter_ipv6, source);
+ }
}
}
+
#endif
return PICO_IP_MULTICAST_EXCLUDE;
}
-static uint8_t pico_mcast_filter_incl_incl(struct pico_mcast_listen *listen)
+static int8_t pico_mcast_filter_incl_incl(struct pico_mcast_listen *listen)
{
/* filter = summation of INCLUDEs */
/* mode stays INCLUDE, add all sources to filter */
struct pico_tree_node *index = NULL, *_tmp = NULL;
union pico_address *source = NULL;
-
+
if( !pico_tree_empty(&listen->MCASTSources)) {
pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
{
source = index->keyValue;
- pico_tree_insert(&MCASTFilter, source);
+ if (pico_tree_insert(&MCASTFilter, source) == &LEAF)
+ return -1;
}
}
+
#ifdef PICO_SUPPORT_IPV6
if( !pico_tree_empty(&listen->MCASTSources_ipv6)) {
pico_tree_foreach_safe(index, &listen->MCASTSources_ipv6, _tmp)
{
source = index->keyValue;
- pico_tree_insert(&MCASTFilter_ipv6, source);
+ if (pico_tree_insert(&MCASTFilter_ipv6, source) == &LEAF)
+ return -1;
}
}
+
#endif
return PICO_IP_MULTICAST_INCLUDE;
}
struct pico_mcast_filter_aggregation
{
- uint8_t (*call)(struct pico_mcast_listen *);
+ int8_t (*call)(struct pico_mcast_listen *);
};
static const struct pico_mcast_filter_aggregation mcast_filter_aggr_call[2][2] =
{
{
- /* EXCL + EXCL */ {.call = pico_mcast_filter_excl_excl},
- /* EXCL + INCL */ {.call = pico_mcast_filter_excl_incl}
+ /* EXCL + EXCL */ {.call = pico_mcast_filter_excl_excl},
+ /* EXCL + INCL */ {.call = pico_mcast_filter_excl_incl}
},
{
- /* INCL + EXCL */ {.call = pico_mcast_filter_incl_excl},
- /* INCL + INCL */ {.call = pico_mcast_filter_incl_incl}
+ /* INCL + EXCL */ {.call = pico_mcast_filter_incl_excl},
+ /* INCL + INCL */ {.call = pico_mcast_filter_incl_incl}
}
};
-static int mcast_aggr_validate(uint8_t fm, struct pico_mcast_listen *l)
+static int mcast_aggr_validate(int8_t fm, struct pico_mcast_listen *l)
{
if (!l)
return -1;
- if (fm > 1)
+ if (fm > 1 || fm < 0)
return -1;
if (l->filter_mode > 1)
@@ -377,7 +412,7 @@ static int mcast_aggr_validate(uint8_t fm, struct pico_mcast_listen *l)
/* MCASTFilter will be empty if no socket is listening on mcast_group on mcast_link anymore */
static int pico_socket_aggregate_mcastfilters(union pico_address *mcast_link, union pico_address *mcast_group)
{
- uint8_t filter_mode = PICO_IP_MULTICAST_INCLUDE;
+ int8_t filter_mode = PICO_IP_MULTICAST_INCLUDE;
struct pico_mcast_listen *listen = NULL;
struct pico_socket *mcast_sock = NULL;
struct pico_tree_node *index = NULL, *_tmp = NULL;
@@ -387,8 +422,9 @@ static int pico_socket_aggregate_mcastfilters(union pico_address *mcast_link, un
pico_tree_foreach_safe(index, &MCASTFilter, _tmp)
{
pico_tree_delete(&MCASTFilter, index->keyValue);
- }
+ }
}
+
#ifdef PICO_SUPPORT_IPV6
if(!pico_tree_empty(&MCASTFilter_ipv6)) {
pico_tree_foreach_safe(index, &MCASTFilter_ipv6, _tmp)
@@ -396,6 +432,7 @@ static int pico_socket_aggregate_mcastfilters(union pico_address *mcast_link, un
pico_tree_delete(&MCASTFilter_ipv6, index->keyValue);
}
}
+
#endif
/* construct new filter */
pico_tree_foreach_safe(index, &MCASTSockets, _tmp)
@@ -407,12 +444,13 @@ static int pico_socket_aggregate_mcastfilters(union pico_address *mcast_link, un
pico_err = PICO_ERR_EINVAL;
return -1;
}
+
if (mcast_filter_aggr_call[filter_mode][listen->filter_mode].call) {
filter_mode = mcast_filter_aggr_call[filter_mode][listen->filter_mode].call(listen);
- if (filter_mode > 1)
+ if (filter_mode > 1 || filter_mode < 0)
return -1;
}
- }
+ }
}
return filter_mode;
}
@@ -432,19 +470,21 @@ static int pico_socket_mcast_filter_include(struct pico_mcast_listen *listen, un
}
}
}
+
#ifdef PICO_SUPPORT_IPV6
if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
pico_tree_foreach(index, &listen->MCASTSources_ipv6)
{
- if (memcmp(&src->ip6 , &((union pico_address *)index->keyValue)->ip6, sizeof(struct pico_ip6))) {
-#ifdef PICO_DEBUG_MCAST
+ if (memcmp(&src->ip6, &((union pico_address *)index->keyValue)->ip6, sizeof(struct pico_ip6))) {
+#ifdef PICO_DEBUG_MCAST
pico_ipv6_to_string(tmp_string, src->ip6.addr);
so_mcast_dbg("MCAST: IP %s in included socket source list\n", tmp_string);
-#endif
+#endif
return 0;
}
}
}
+
#endif
/* XXX IPV6 ADDRESS */
so_mcast_dbg("MCAST: IP %08X NOT in included socket source list\n", src->ip4.addr);
@@ -467,19 +507,21 @@ static int pico_socket_mcast_filter_exclude(struct pico_mcast_listen *listen, un
}
}
}
+
#ifdef PICO_SUPPORT_IPV6
if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
pico_tree_foreach(index, &listen->MCASTSources_ipv6)
{
- if (memcmp(&src->ip6 , &((union pico_address *)index->keyValue)->ip6, sizeof(struct pico_ip6))) {
-#ifdef PICO_DEBUG_MCAST
+ if (memcmp(&src->ip6, &((union pico_address *)index->keyValue)->ip6, sizeof(struct pico_ip6))) {
+#ifdef PICO_DEBUG_MCAST
pico_ipv6_to_string(tmp_string, src->ip6.addr);
so_mcast_dbg("MCAST: IP %s in excluded socket source list\n", tmp_string);
-#endif
+#endif
return 0;
}
}
}
+
#endif
/* XXX IPV6 ADDRESS */
so_mcast_dbg("MCAST: IP %08X NOT in excluded socket source list\n", src->ip4.addr);
@@ -503,12 +545,14 @@ static void *pico_socket_mcast_filter_link_get(struct pico_socket *s)
/* check if no multicast enabled on socket */
if (!s->MCASTListen)
return NULL;
- if( IS_SOCK_IPV4(s) ) {
+
+ if( IS_SOCK_IPV4(s)) {
if (!s->local_addr.ip4.addr)
return pico_ipv4_get_default_mcastlink();
return pico_ipv4_link_get(&s->local_addr.ip4);
}
+
#ifdef PICO_SUPPORT_IPV6
else if( IS_SOCK_IPV6(s)) {
if (pico_ipv6_is_null_address(&s->local_addr.ip6))
@@ -527,10 +571,12 @@ int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_gr
mcast_link = pico_socket_mcast_filter_link_get(s);
if (!mcast_link)
return -1;
+
if(IS_SOCK_IPV4(s))
- listen = listen_find(s,(union pico_address *) &((struct pico_ipv4_link*)(mcast_link))->address, mcast_group);
+ listen = listen_find(s, (union pico_address *) &((struct pico_ipv4_link*)(mcast_link))->address, mcast_group);
+
#ifdef PICO_SUPPORT_IPV6
- else if(IS_SOCK_IPV6(s))
+ else if(IS_SOCK_IPV6(s))
listen = listen_find(s, (union pico_address *)&((struct pico_ipv6_link*)(mcast_link))->address, mcast_group);
#endif
if (!listen)
@@ -540,27 +586,31 @@ int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_gr
}
-static struct pico_ipv4_link *get_mcast_link(union pico_address *a) {
+static struct pico_ipv4_link *get_mcast_link(union pico_address *a)
+{
if (!a->ip4.addr)
return pico_ipv4_get_default_mcastlink();
+
return pico_ipv4_link_get(&a->ip4);
}
#ifdef PICO_SUPPORT_IPV6
-static struct pico_ipv6_link *get_mcast_link_ipv6(union pico_address *a) {
+static struct pico_ipv6_link *get_mcast_link_ipv6(union pico_address *a)
+{
if (pico_ipv6_is_null_address(&a->ip6)) {
return pico_ipv6_get_default_mcastlink();
}
+
return pico_ipv6_link_get(&a->ip6);
}
#endif
static int pico_socket_setoption_pre_validation(struct pico_ip_mreq *mreq)
- {
- if (!mreq)
+{
+ if (!mreq)
return -1;
- if (!mreq->mcast_group_addr.ip4.addr)
+ if (!mreq->mcast_group_addr.ip4.addr)
return -1;
return 0;
@@ -580,10 +630,10 @@ static int pico_socket_setoption_pre_validation_ipv6(struct pico_ip_mreq *mreq)
static struct pico_ipv4_link *pico_socket_setoption_validate_mreq(struct pico_ip_mreq *mreq)
{
- if (pico_socket_setoption_pre_validation(mreq) < 0)
+ if (pico_socket_setoption_pre_validation(mreq) < 0)
return NULL;
- if (pico_ipv4_is_unicast(mreq->mcast_group_addr.ip4.addr))
+ if (pico_ipv4_is_unicast(mreq->mcast_group_addr.ip4.addr))
return NULL;
return get_mcast_link((union pico_address *)&mreq->mcast_link_addr);
@@ -597,6 +647,7 @@ static struct pico_ipv6_link *pico_socket_setoption_validate_mreq_ipv6(struct pi
if (pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_group_addr))
return NULL;
+
return get_mcast_link_ipv6((union pico_address *)&mreq->mcast_link_addr);
}
#endif
@@ -643,10 +694,12 @@ static struct pico_ipv6_link *pico_socket_setoption_validate_s_mreq_ipv6(struct
if (pico_socket_setoption_pre_validation_s_ipv6(mreq) < 0) {
return NULL;
}
- if (pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_group_addr)){
+
+ if (pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_group_addr)) {
return NULL;
}
- if (!pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_source_addr)){
+
+ if (!pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_source_addr)) {
return NULL;
}
@@ -663,14 +716,15 @@ static struct pico_ipv4_link *setop_multicast_link_search(void *value, int bysou
if (!bysource) {
mreq = (struct pico_ip_mreq *)value;
mcast_link = pico_socket_setoption_validate_mreq(mreq);
- if (!mcast_link)
- return NULL;
+ if (!mcast_link)
+ return NULL;
+
if (!mreq->mcast_link_addr.ip4.addr)
mreq->mcast_link_addr.ip4.addr = mcast_link->address.addr;
} else {
mreq_src = (struct pico_ip_mreq_source *)value;
if (!mreq_src) {
- return NULL;
+ return NULL;
}
mcast_link = pico_socket_setoption_validate_s_mreq(mreq_src);
@@ -694,8 +748,9 @@ static struct pico_ipv6_link *setop_multicast_link_search_ipv6(void *value, int
mreq = (struct pico_ip_mreq *)value;
mcast_link = pico_socket_setoption_validate_mreq_ipv6(mreq);
if (!mcast_link) {
- return NULL;
+ return NULL;
}
+
if (pico_ipv6_is_null_address(&mreq->mcast_link_addr.ip6))
mreq->mcast_link_addr.ip6 = mcast_link->address;
} else {
@@ -708,9 +763,11 @@ static struct pico_ipv6_link *setop_multicast_link_search_ipv6(void *value, int
if (!mcast_link) {
return NULL;
}
+
if (pico_ipv6_is_null_address(&mreq_src->mcast_link_addr.ip6))
mreq_src->mcast_link_addr.ip6 = mcast_link->address;
}
+
return mcast_link;
}
#endif
@@ -719,7 +776,7 @@ static int setop_verify_listen_tree(struct pico_socket *s, int alloc)
if(!alloc)
return -1;
- if( IS_SOCK_IPV4(s) ) {
+ if( IS_SOCK_IPV4(s)) {
s->MCASTListen = PICO_ZALLOC(sizeof(struct pico_tree));
if (!s->MCASTListen) {
@@ -730,9 +787,10 @@ static int setop_verify_listen_tree(struct pico_socket *s, int alloc)
s->MCASTListen->root = &LEAF;
s->MCASTListen->compare = mcast_listen_cmp;
return 0;
- }
+ }
+
#ifdef PICO_SUPPORT_IPV6
- else if( IS_SOCK_IPV6(s)){
+ else if( IS_SOCK_IPV6(s)) {
s->MCASTListen_ipv6 = PICO_ZALLOC(sizeof(struct pico_tree));
if (!s->MCASTListen_ipv6) {
pico_err = PICO_ERR_ENOMEM;
@@ -757,21 +815,25 @@ static void *setopt_multicast_check(struct pico_socket *s, void *value, int allo
pico_err = PICO_ERR_EINVAL;
return NULL;
}
+
if(IS_SOCK_IPV4(s))
mcast_link = setop_multicast_link_search(value, bysource);
+
#ifdef PICO_SUPPORT_IPV6
else if(IS_SOCK_IPV6(s))
mcast_link = setop_multicast_link_search_ipv6(value, bysource);
-#endif
+#endif
if (!mcast_link) {
pico_err = PICO_ERR_EINVAL;
return NULL;
}
+
if (!listen_tree) { /* No RBTree allocated yet */
if (setop_verify_listen_tree(s, alloc) < 0) {
return NULL;
}
}
+
return mcast_link;
}
@@ -794,20 +856,23 @@ void pico_multicast_delete(struct pico_socket *s)
if (tree) {
pico_tree_foreach_safe(index2, tree, _tmp2)
{
- source = index->keyValue;
+ source = index2->keyValue;
pico_tree_delete(tree, source);
PICO_FREE(source);
}
}
+
filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&listen->mcast_link, (union pico_address *)&listen->mcast_group);
if (filter_mode >= 0) {
if(IS_SOCK_IPV4(s))
pico_ipv4_mcast_leave(&listen->mcast_link.ip4, &listen->mcast_group.ip4, 1, (uint8_t)filter_mode, &MCASTFilter);
-#ifdef PICO_SUPPORT_IPV6
- else if(IS_SOCK_IPV6(s))
+
+#ifdef PICO_SUPPORT_IPV6
+ else if(IS_SOCK_IPV6(s))
pico_ipv6_mcast_leave(&listen->mcast_link.ip6, &listen->mcast_group.ip6, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
#endif
}
+
pico_tree_delete(listen_tree, listen);
PICO_FREE(listen);
}
@@ -867,19 +932,23 @@ static int mcast_so_loop(struct pico_socket *s, void *value)
pico_err = PICO_ERR_EINVAL;
return -1;
}
-static int mcast_get_param(struct pico_mcast *mcast, struct pico_socket *s, void *value,int alloc, int by_source) {
+static int mcast_get_param(struct pico_mcast *mcast, struct pico_socket *s, void *value, int alloc, int by_source)
+{
if(by_source)
mcast->mreq_s = (struct pico_ip_mreq_source *)value;
- else
+ else
mcast->mreq = (struct pico_ip_mreq *)value;
+
mcast->mcast_link = setopt_multicast_check(s, value, alloc, by_source);
if (!mcast->mcast_link)
return -1;
- mcast->address = pico_mcast_get_link_address(s, mcast->mcast_link);
+
+ mcast->address = pico_mcast_get_link_address(s, mcast->mcast_link);
if(by_source)
mcast->listen = listen_find(s, &(mcast->mreq_s)->mcast_link_addr, &mcast->mreq_s->mcast_group_addr);
else
mcast->listen = listen_find(s, &(mcast->mreq)->mcast_link_addr, &mcast->mreq->mcast_group_addr);
+
return 0;
}
static int mcast_so_addm(struct pico_socket *s, void *value)
@@ -887,23 +956,26 @@ static int mcast_so_addm(struct pico_socket *s, void *value)
int filter_mode = 0;
struct pico_mcast mcast;
struct pico_tree *tree, *listen_tree;
- if(mcast_get_param(&mcast, s, value, 1,0) < 0)
+ if(mcast_get_param(&mcast, s, value, 1, 0) < 0)
return -1;
-
+
if (mcast.listen) {
- if (mcast.listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) {
+ if (mcast.listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) {
so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n");
- } else {
+ } else {
so_mcast_dbg("pico_socket_setoption: ERROR duplicate PICO_IP_ADD_MEMBERSHIP\n");
}
+
pico_err = PICO_ERR_EINVAL;
return -1;
- }
+ }
+
mcast.listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen));
if (!mcast.listen) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
+
mcast.listen->filter_mode = PICO_IP_MULTICAST_EXCLUDE;
mcast.listen->mcast_link = mcast.mreq->mcast_link_addr;
mcast.listen->mcast_group = mcast.mreq->mcast_group_addr;
@@ -912,22 +984,33 @@ static int mcast_so_addm(struct pico_socket *s, void *value)
tree = mcast_get_src_tree(s, &mcast);
listen_tree = mcast_get_listen_tree(s);
#ifdef PICO_SUPPORT_IPV6
- if( IS_SOCK_IPV6(s))
+ if( IS_SOCK_IPV6(s))
mcast.listen->proto = PICO_PROTO_IPV6;
+
#endif
tree->root = &LEAF;
- pico_tree_insert(listen_tree, mcast.listen);
-
- pico_tree_insert(&MCASTSockets, s);
+ if (pico_tree_insert(listen_tree, mcast.listen)) {
+ PICO_FREE(mcast.listen);
+ return -1;
+ }
+
+ if (pico_tree_insert(&MCASTSockets, s) == &LEAF) {
+ pico_tree_delete(listen_tree, mcast.listen);
+ PICO_FREE(mcast.listen);
+ return -1;
+ }
+
filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq->mcast_group_addr);
if (filter_mode < 0)
return -1;
+
so_mcast_dbg("PICO_IP_ADD_MEMBERSHIP - success, added %p\n", s);
- if(IS_SOCK_IPV4(s))
- return pico_ipv4_mcast_join((struct pico_ip4*)&mcast.mreq->mcast_link_addr,(struct pico_ip4*) &mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
-#ifdef PICO_SUPPORT_IPV6
- else if(IS_SOCK_IPV6(s)) {
- return pico_ipv6_mcast_join((struct pico_ip6*)&mcast.mreq->mcast_link_addr,(struct pico_ip6*)&mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+ if(IS_SOCK_IPV4(s))
+ return pico_ipv4_mcast_join((struct pico_ip4*)&mcast.mreq->mcast_link_addr, (struct pico_ip4*) &mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
+
+#ifdef PICO_SUPPORT_IPV6
+ else if(IS_SOCK_IPV6(s)) {
+ return pico_ipv6_mcast_join((struct pico_ip6*)&mcast.mreq->mcast_link_addr, (struct pico_ip6*)&mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
}
#endif
return -1;
@@ -937,17 +1020,19 @@ static int mcast_so_dropm(struct pico_socket *s, void *value)
{
int filter_mode = 0;
union pico_address *source = NULL;
- struct pico_tree_node *_tmp,*index;
+ struct pico_tree_node *_tmp, *index;
struct pico_mcast mcast;
- struct pico_tree *listen_tree,*tree;
- if(mcast_get_param(&mcast, s, value, 0,0) < 0)
+ struct pico_tree *listen_tree, *tree;
+ if(mcast_get_param(&mcast, s, value, 0, 0) < 0)
return -1;
+
if (!mcast.listen) {
so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_DROP_MEMBERSHIP before PICO_IP_ADD_MEMBERSHIP/SOURCE_MEMBERSHIP\n");
pico_err = PICO_ERR_EADDRNOTAVAIL;
return -1;
- }
- tree = mcast_get_src_tree(s,&mcast);
+ }
+
+ tree = mcast_get_src_tree(s, &mcast);
listen_tree = mcast_get_listen_tree(s);
pico_tree_foreach_safe(index, tree, _tmp)
@@ -962,15 +1047,17 @@ static int mcast_so_dropm(struct pico_socket *s, void *value)
mcast_set_listen_tree_p_null(s);
pico_tree_delete(&MCASTSockets, s);
}
-
+
filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq->mcast_group_addr);
if (filter_mode < 0)
return -1;
- if(IS_SOCK_IPV4(s))
- return pico_ipv4_mcast_leave((struct pico_ip4*) &mcast.mreq->mcast_link_addr,(struct pico_ip4 *) &mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
-#ifdef PICO_SUPPORT_IPV6
+
+ if(IS_SOCK_IPV4(s))
+ return pico_ipv4_mcast_leave((struct pico_ip4*) &mcast.mreq->mcast_link_addr, (struct pico_ip4 *) &mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
+
+#ifdef PICO_SUPPORT_IPV6
else if(IS_SOCK_IPV6(s)) { }
- return pico_ipv6_mcast_leave((struct pico_ip6*)&mcast.mreq->mcast_link_addr,(struct pico_ip6*)&mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+ return pico_ipv6_mcast_leave((struct pico_ip6*)&mcast.mreq->mcast_link_addr, (struct pico_ip6*)&mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
#endif
return -1;
}
@@ -980,15 +1067,16 @@ static int mcast_so_unblock_src(struct pico_socket *s, void *value)
int filter_mode = 0;
union pico_address stest, *source = NULL;
struct pico_mcast mcast;
- if(mcast_get_param(&mcast, s, value, 0,1) < 0)
+ if(mcast_get_param(&mcast, s, value, 0, 1) < 0)
return -1;
-
+
memset(&stest, 0, sizeof(union pico_address));
if (!mcast.listen) {
so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_UNBLOCK_SOURCE before PICO_IP_ADD_MEMBERSHIP\n");
pico_err = PICO_ERR_EINVAL;
return -1;
- }
+ }
+
if (mcast.listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) {
so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n");
pico_err = PICO_ERR_EINVAL;
@@ -998,7 +1086,8 @@ static int mcast_so_unblock_src(struct pico_socket *s, void *value)
stest = mcast.mreq_s->mcast_source_addr;
if( IS_SOCK_IPV4(s))
source = pico_tree_findKey(&mcast.listen->MCASTSources, &stest);
-#ifdef PICO_SUPPORT_IPV6
+
+#ifdef PICO_SUPPORT_IPV6
else if( IS_SOCK_IPV6(s))
source = pico_tree_findKey(&mcast.listen->MCASTSources_ipv6, &stest);
#endif
@@ -1006,22 +1095,26 @@ static int mcast_so_unblock_src(struct pico_socket *s, void *value)
so_mcast_dbg("pico_socket_setoption: ERROR address to unblock not in source list\n");
pico_err = PICO_ERR_EADDRNOTAVAIL;
return -1;
- }
- if( IS_SOCK_IPV4(s) )
+ }
+
+ if( IS_SOCK_IPV4(s))
pico_tree_delete(&mcast.listen->MCASTSources, source);
+
#ifdef PICO_SUPPORT_IPV6
- else if( IS_SOCK_IPV6(s) )
+ else if( IS_SOCK_IPV6(s))
pico_tree_delete(&mcast.listen->MCASTSources_ipv6, source);
#endif
filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->mcast_group_addr);
if (filter_mode < 0)
return -1;
- if(IS_SOCK_IPV4(s))
- return pico_ipv4_mcast_leave((struct pico_ip4 *)&mcast.mreq_s->mcast_link_addr,(struct pico_ip4*) &mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter);
-#ifdef PICO_SUPPORT_IPV6
+
+ if(IS_SOCK_IPV4(s))
+ return pico_ipv4_mcast_leave((struct pico_ip4 *)&mcast.mreq_s->mcast_link_addr, (struct pico_ip4*) &mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter);
+
+#ifdef PICO_SUPPORT_IPV6
else if(IS_SOCK_IPV6(s)) { }
- return pico_ipv6_mcast_leave((struct pico_ip6*)&mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+ return pico_ipv6_mcast_leave((struct pico_ip6*)&mcast.mreq_s->mcast_link_addr, (struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter_ipv6);
#endif
return -1;
}
@@ -1031,24 +1124,27 @@ static int mcast_so_block_src(struct pico_socket *s, void *value)
int filter_mode = 0;
union pico_address stest, *source = NULL;
struct pico_mcast mcast;
- if(mcast_get_param(&mcast, s, value, 0,1) < 0)
+ if(mcast_get_param(&mcast, s, value, 0, 1) < 0)
return -1;
-
+
memset(&stest, 0, sizeof(union pico_address));
if (!mcast.listen) {
dbg("pico_socket_setoption: ERROR PICO_IP_BLOCK_SOURCE before PICO_IP_ADD_MEMBERSHIP\n");
pico_err = PICO_ERR_EINVAL;
return -1;
- }
+ }
+
if (mcast.listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) {
so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n");
pico_err = PICO_ERR_EINVAL;
return -1;
}
+
stest = mcast.mreq_s->mcast_source_addr;
if( IS_SOCK_IPV4(s))
source = pico_tree_findKey(&mcast.listen->MCASTSources, &stest);
-#ifdef PICO_SUPPORT_IPV6
+
+#ifdef PICO_SUPPORT_IPV6
else if( IS_SOCK_IPV6(s))
source = pico_tree_findKey(&mcast.listen->MCASTSources_ipv6, &stest);
#endif
@@ -1056,28 +1152,40 @@ static int mcast_so_block_src(struct pico_socket *s, void *value)
so_mcast_dbg("pico_socket_setoption: ERROR address to block already in source list\n");
pico_err = PICO_ERR_ENOMEM;
return -1;
- }
+ }
+
source = PICO_ZALLOC(sizeof(union pico_address));
if (!source) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
+
*source = mcast.mreq_s->mcast_source_addr;
- if( IS_SOCK_IPV4(s) )
- pico_tree_insert(&mcast.listen->MCASTSources, source);
-#ifdef PICO_SUPPORT_IPV6
- else if( IS_SOCK_IPV6(s) )
- pico_tree_insert(&mcast.listen->MCASTSources_ipv6, source);
+ if( IS_SOCK_IPV4(s)) {
+ if (pico_tree_insert(&mcast.listen->MCASTSources, source)) {
+ PICO_FREE(source);
+ return -1;
+ }
+ }
+
+#ifdef PICO_SUPPORT_IPV6
+ else if( IS_SOCK_IPV6(s))
+ if (pico_tree_insert(&mcast.listen->MCASTSources_ipv6, source)) {
+ PICO_FREE(source);
+ return -1;
+ }
#endif
filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->mcast_group_addr);
if (filter_mode < 0)
return -1;
- if(IS_SOCK_IPV4(s))
+
+ if(IS_SOCK_IPV4(s))
return pico_ipv4_mcast_join((struct pico_ip4 *) &mcast.mreq_s->mcast_link_addr, (struct pico_ip4*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter);
-#ifdef PICO_SUPPORT_IPV6
+
+#ifdef PICO_SUPPORT_IPV6
else if(IS_SOCK_IPV6(s)) { }
- return pico_ipv6_mcast_join((struct pico_ip6 *)&mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+ return pico_ipv6_mcast_join((struct pico_ip6 *)&mcast.mreq_s->mcast_link_addr, (struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter_ipv6);
#endif
return -1;
}
@@ -1087,41 +1195,48 @@ static int mcast_so_addsrcm(struct pico_socket *s, void *value)
int filter_mode = 0, reference_count = 0;
union pico_address stest, *source = NULL;
struct pico_mcast mcast;
- struct pico_tree *tree,*listen_tree;
- if(mcast_get_param(&mcast, s, value, 1,1) < 0)
+ struct pico_tree *tree, *listen_tree;
+ if(mcast_get_param(&mcast, s, value, 1, 1) < 0)
return -1;
memset(&stest, 0, sizeof(union pico_address));
listen_tree = mcast_get_listen_tree(s);
if (mcast.listen) {
- tree = mcast_get_src_tree(s,&mcast);
+ tree = mcast_get_src_tree(s, &mcast);
if (mcast.listen->filter_mode != PICO_IP_MULTICAST_INCLUDE) {
so_mcast_dbg("pico_socket_setoption: ERROR source-specific multicast (include) on any-source multicast (exclude)\n");
pico_err = PICO_ERR_EINVAL;
return -1;
}
+
stest = mcast.mreq_s->mcast_source_addr;
source = pico_tree_findKey(tree, &stest);
if (source) {
so_mcast_dbg("pico_socket_setoption: ERROR source address to allow already in source list\n");
- pico_err = PICO_ERR_ENOMEM;
- return -1;
- }
- source = PICO_ZALLOC(sizeof(union pico_address));
- if (!source) {
pico_err = PICO_ERR_EADDRNOTAVAIL;
return -1;
}
+
+ source = PICO_ZALLOC(sizeof(union pico_address));
+ if (!source) {
+ pico_err = PICO_ERR_ENOMEM;
+ return -1;
+ }
+
*source = mcast.mreq_s->mcast_source_addr;
- pico_tree_insert(tree, source);
-
+ if (pico_tree_insert(tree, source)) {
+ PICO_FREE(source);
+ return -1;
+ }
+
} else {
mcast.listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen));
if (!mcast.listen) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
- tree = mcast_get_src_tree(s,&mcast);
+
+ tree = mcast_get_src_tree(s, &mcast);
mcast.listen->filter_mode = PICO_IP_MULTICAST_INCLUDE;
mcast.listen->mcast_link = mcast.mreq_s->mcast_link_addr;
mcast.listen->mcast_group = mcast.mreq_s->mcast_group_addr;
@@ -1132,24 +1247,42 @@ static int mcast_so_addsrcm(struct pico_socket *s, void *value)
pico_err = PICO_ERR_ENOMEM;
return -1;
}
+
#ifdef PICO_SUPPORT_IPV6
- if( IS_SOCK_IPV6(s))
+ if( IS_SOCK_IPV6(s))
mcast.listen->proto = PICO_PROTO_IPV6;
+
#endif
*source = mcast.mreq_s->mcast_source_addr;
- pico_tree_insert(tree, source);
- pico_tree_insert(listen_tree, mcast.listen);
+ if (pico_tree_insert(tree, source)) {
+ PICO_FREE(mcast.listen);
+ PICO_FREE(source);
+ return -1;
+ }
+
+ if (pico_tree_insert(listen_tree, mcast.listen)) {
+ pico_tree_delete(tree, source);
+ PICO_FREE(source);
+ PICO_FREE(mcast.listen);
+ return -1;
+ }
reference_count = 1;
}
- pico_tree_insert(&MCASTSockets, s);
+
+ if (pico_tree_insert(&MCASTSockets, s) == &LEAF) {
+ return -1;
+ }
+
filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->mcast_group_addr);
if (filter_mode < 0)
return -1;
- if(IS_SOCK_IPV4(s))
+
+ if(IS_SOCK_IPV4(s))
return pico_ipv4_mcast_join((struct pico_ip4 *)&mcast.mreq_s->mcast_link_addr, (struct pico_ip4*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter);
-#ifdef PICO_SUPPORT_IPV6
+
+#ifdef PICO_SUPPORT_IPV6
else if(IS_SOCK_IPV6(s)) { }
- return pico_ipv6_mcast_join((struct pico_ip6 *) &mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+ return pico_ipv6_mcast_join((struct pico_ip6 *) &mcast.mreq_s->mcast_link_addr, (struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter_ipv6);
#endif
return -1;
}
@@ -1159,8 +1292,8 @@ static int mcast_so_dropsrcm(struct pico_socket *s, void *value)
int filter_mode = 0, reference_count = 0;
union pico_address stest, *source = NULL;
struct pico_mcast mcast;
- struct pico_tree *tree,*listen_tree;
- if(mcast_get_param(&mcast, s, value, 0,1) < 0)
+ struct pico_tree *tree, *listen_tree;
+ if(mcast_get_param(&mcast, s, value, 0, 1) < 0)
return -1;
memset(&stest, 0, sizeof(union pico_address));
@@ -1169,12 +1302,14 @@ static int mcast_so_dropsrcm(struct pico_socket *s, void *value)
so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_DROP_SOURCE_MEMBERSHIP before PICO_IP_ADD_SOURCE_MEMBERSHIP\n");
pico_err = PICO_ERR_EADDRNOTAVAIL;
return -1;
- }
+ }
+
if (mcast.listen->filter_mode != PICO_IP_MULTICAST_INCLUDE) {
so_mcast_dbg("pico_socket_setoption: ERROR source-specific multicast (include) on any-source multicast (exclude)\n");
pico_err = PICO_ERR_EINVAL;
return -1;
}
+
tree = mcast_get_src_tree(s, &mcast);
stest = mcast.mreq_s->mcast_source_addr;
source = pico_tree_findKey(tree, &stest);
@@ -1182,7 +1317,8 @@ static int mcast_so_dropsrcm(struct pico_socket *s, void *value)
so_mcast_dbg("pico_socket_setoption: ERROR address to drop not in source list\n");
pico_err = PICO_ERR_EADDRNOTAVAIL;
return -1;
- }
+ }
+
pico_tree_delete(tree, source);
if (pico_tree_empty(tree)) { /* 1 if empty, 0 otherwise */
reference_count = 1;
@@ -1198,11 +1334,13 @@ static int mcast_so_dropsrcm(struct pico_socket *s, void *value)
filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->mcast_group_addr);
if (filter_mode < 0)
return -1;
- if(IS_SOCK_IPV4(s))
+
+ if(IS_SOCK_IPV4(s))
return pico_ipv4_mcast_leave((struct pico_ip4 *) &mcast.mreq_s->mcast_link_addr, (struct pico_ip4*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter);
-#ifdef PICO_SUPPORT_IPV6
+
+#ifdef PICO_SUPPORT_IPV6
else if(IS_SOCK_IPV6(s)) { }
- return pico_ipv6_mcast_leave((struct pico_ip6 *)&mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+ return pico_ipv6_mcast_leave((struct pico_ip6 *)&mcast.mreq_s->mcast_link_addr, (struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter_ipv6);
#endif
return -1;
}
diff --git a/ext/picotcp/stack/pico_stack.c b/ext/picotcp/stack/pico_stack.c
index 292ea5a..e2d049b 100644
--- a/ext/picotcp/stack/pico_stack.c
+++ b/ext/picotcp/stack/pico_stack.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
@@ -16,6 +16,9 @@
#include "pico_addressing.h"
#include "pico_dns_client.h"
+#include "pico_6lowpan_ll.h"
+#include "pico_ethernet.h"
+#include "pico_6lowpan.h"
#include "pico_olsr.h"
#include "pico_aodv.h"
#include "pico_eth.h"
@@ -30,22 +33,11 @@
#include "pico_socket.h"
#include "heap.h"
-#define IS_LIMITED_BCAST(f) (((struct pico_ipv4_hdr *) f->net_hdr)->dst.addr == PICO_IP4_BCAST)
-
-const uint8_t PICO_ETHADDR_ALL[6] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-};
-
-# define PICO_SIZE_MCAST 3
-const uint8_t PICO_ETHADDR_MCAST[6] = {
- 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
-};
-
-#ifdef PICO_SUPPORT_IPV6
-# define PICO_SIZE_MCAST6 2
-const uint8_t PICO_ETHADDR_MCAST6[6] = {
- 0x33, 0x33, 0x00, 0x00, 0x00, 0x00
-};
+/* Mockables */
+#if defined UNIT_TEST
+# define MOCKABLE __attribute__((weak))
+#else
+# define MOCKABLE
#endif
@@ -190,8 +182,10 @@ int pico_notify_pkt_too_big(struct pico_frame *f)
return 0;
}
+/*******************************************************************************
+ * TRANSPORT LAYER
+ ******************************************************************************/
-/* Transport layer */
MOCKABLE int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto)
{
int32_t ret = -1;
@@ -238,7 +232,11 @@ MOCKABLE int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto)
return ret;
}
-int32_t pico_network_receive(struct pico_frame *f)
+/*******************************************************************************
+ * NETWORK LAYER
+ ******************************************************************************/
+
+MOCKABLE int32_t pico_network_receive(struct pico_frame *f)
{
if (0) {}
@@ -260,7 +258,7 @@ int32_t pico_network_receive(struct pico_frame *f)
return (int32_t)f->buffer_len;
}
-/* Network layer: interface towards socket for frame sending */
+/// Interface towards socket for frame sending
int32_t pico_network_send(struct pico_frame *f)
{
if (!f || !f->sock || !f->sock->net) {
@@ -295,350 +293,6 @@ int pico_source_is_local(struct pico_frame *f)
return 0;
}
-#ifdef PICO_SUPPORT_ETH
-/* DATALINK LEVEL: interface from network to the device
- * and vice versa.
- */
-
-/* The pico_ethernet_receive() function is used by
- * those devices supporting ETH in order to push packets up
- * into the stack.
- */
-
-static int destination_is_bcast(struct pico_frame *f)
-{
- if (!f)
- return 0;
-
- if (IS_IPV6(f))
- return 0;
-
-#ifdef PICO_SUPPORT_IPV4
- else {
- struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
- return pico_ipv4_is_broadcast(hdr->dst.addr);
- }
-#else
- return 0;
-#endif
-}
-
-static int destination_is_mcast(struct pico_frame *f)
-{
- int ret = 0;
- if (!f)
- return 0;
-
-#ifdef PICO_SUPPORT_IPV6
- if (IS_IPV6(f)) {
- struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *) f->net_hdr;
- ret = pico_ipv6_is_multicast(hdr->dst.addr);
- }
-
-#endif
-#ifdef PICO_SUPPORT_IPV4
- else {
- struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
- ret = pico_ipv4_is_multicast(hdr->dst.addr);
- }
-#endif
-
- return ret;
-}
-
-#ifdef PICO_SUPPORT_IPV4
-static int32_t pico_ipv4_ethernet_receive(struct pico_frame *f)
-{
- if (IS_IPV4(f)) {
- pico_enqueue(pico_proto_ipv4.q_in, f);
- } else {
- (void)pico_icmp4_param_problem(f, 0);
- pico_frame_discard(f);
- return -1;
- }
-
- return (int32_t)f->buffer_len;
-}
-#endif
-
-#ifdef PICO_SUPPORT_IPV6
-static int32_t pico_ipv6_ethernet_receive(struct pico_frame *f)
-{
- if (IS_IPV6(f)) {
- pico_enqueue(pico_proto_ipv6.q_in, f);
- } else {
- /* Wrong version for link layer type */
- pico_frame_discard(f);
- return -1;
- }
-
- return (int32_t)f->buffer_len;
-}
-#endif
-
-static int32_t pico_ll_receive(struct pico_frame *f)
-{
- struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
- f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr);
-
-#if (defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH)
- if (hdr->proto == PICO_IDETH_ARP)
- return pico_arp_receive(f);
-
-#endif
-
-#if defined (PICO_SUPPORT_IPV4)
- if (hdr->proto == PICO_IDETH_IPV4)
- return pico_ipv4_ethernet_receive(f);
-
-#endif
-
-#if defined (PICO_SUPPORT_IPV6)
- if (hdr->proto == PICO_IDETH_IPV6)
- return pico_ipv6_ethernet_receive(f);
-
-#endif
-
- pico_frame_discard(f);
- return -1;
-}
-
-static void pico_ll_check_bcast(struct pico_frame *f)
-{
- struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
- /* Indicate a link layer broadcast packet */
- if (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) == 0)
- f->flags |= PICO_FRAME_FLAG_BCAST;
-}
-
-int32_t pico_ethernet_receive(struct pico_frame *f)
-{
- struct pico_eth_hdr *hdr;
- if (!f || !f->dev || !f->datalink_hdr)
- {
- pico_frame_discard(f);
- return -1;
- }
-
- hdr = (struct pico_eth_hdr *) f->datalink_hdr;
- if ((memcmp(hdr->daddr, f->dev->eth->mac.addr, PICO_SIZE_ETH) != 0) &&
- (memcmp(hdr->daddr, PICO_ETHADDR_MCAST, PICO_SIZE_MCAST) != 0) &&
-#ifdef PICO_SUPPORT_IPV6
- (memcmp(hdr->daddr, PICO_ETHADDR_MCAST6, PICO_SIZE_MCAST6) != 0) &&
-#endif
- (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) != 0))
- {
- pico_frame_discard(f);
- return -1;
- }
-
- pico_ll_check_bcast(f);
- return pico_ll_receive(f);
-}
-
-static struct pico_eth *pico_ethernet_mcast_translate(struct pico_frame *f, uint8_t *pico_mcast_mac)
-{
- struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
-
- /* place 23 lower bits of IP in lower 23 bits of MAC */
- pico_mcast_mac[5] = (long_be(hdr->dst.addr) & 0x000000FFu);
- pico_mcast_mac[4] = (uint8_t)((long_be(hdr->dst.addr) & 0x0000FF00u) >> 8u);
- pico_mcast_mac[3] = (uint8_t)((long_be(hdr->dst.addr) & 0x007F0000u) >> 16u);
-
- return (struct pico_eth *)pico_mcast_mac;
-}
-
-
-#ifdef PICO_SUPPORT_IPV6
-static struct pico_eth *pico_ethernet_mcast6_translate(struct pico_frame *f, uint8_t *pico_mcast6_mac)
-{
- struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
-
- /* first 2 octets are 0x33, last four are the last four of dst */
- pico_mcast6_mac[5] = hdr->dst.addr[PICO_SIZE_IP6 - 1];
- pico_mcast6_mac[4] = hdr->dst.addr[PICO_SIZE_IP6 - 2];
- pico_mcast6_mac[3] = hdr->dst.addr[PICO_SIZE_IP6 - 3];
- pico_mcast6_mac[2] = hdr->dst.addr[PICO_SIZE_IP6 - 4];
-
- return (struct pico_eth *)pico_mcast6_mac;
-}
-#endif
-
-static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const dstmac)
-{
- int retval = -1;
- if (!dstmac)
- return -1;
-
- #ifdef PICO_SUPPORT_IPV6
- if (destination_is_mcast(f)) {
- uint8_t pico_mcast6_mac[6] = {
- 0x33, 0x33, 0x00, 0x00, 0x00, 0x00
- };
- pico_ethernet_mcast6_translate(f, pico_mcast6_mac);
- memcpy(dstmac, pico_mcast6_mac, PICO_SIZE_ETH);
- retval = 0;
- } else {
- struct pico_eth *neighbor = pico_ipv6_get_neighbor(f);
- if (neighbor)
- {
- memcpy(dstmac, neighbor, PICO_SIZE_ETH);
- retval = 0;
- }
- }
-
- #else
- (void)f;
- pico_err = PICO_ERR_EPROTONOSUPPORT;
- #endif
- return retval;
-}
-
-
-/* Ethernet send, first attempt: try our own address.
- * Returns 0 if the packet is not for us.
- * Returns 1 if the packet is cloned to our own receive queue, so the caller can discard the original frame.
- * */
-static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr)
-{
- if (!hdr)
- return 0;
-
- /* Check own mac */
- if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) {
- struct pico_frame *clone = pico_frame_copy(f);
- dbg("sending out packet destined for our own mac\n");
- (void)pico_ethernet_receive(clone);
- return 1;
- }
-
- return 0;
-}
-
-/* Ethernet send, second attempt: try bcast.
- * Returns 0 if the packet is not bcast, so it will be handled somewhere else.
- * Returns 1 if the packet is handled by the pico_device_broadcast() function, so it can be discarded.
- * */
-static int32_t pico_ethsend_bcast(struct pico_frame *f)
-{
- if (IS_LIMITED_BCAST(f)) {
- (void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */
- return 1;
- }
-
- return 0;
-}
-
-/* Ethernet send, third attempt: try unicast.
- * If the device driver is busy, we return 0, so the stack won't discard the frame.
- * In case of success, we can safely return 1.
- */
-static int32_t pico_ethsend_dispatch(struct pico_frame *f)
-{
- int ret = f->dev->send(f->dev, f->start, (int) f->len);
- if (ret <= 0)
- return 0; /* Failure to deliver! */
- else {
- return 1; /* Frame is in flight by now. */
- }
-}
-
-
-
-
-/* This function looks for the destination mac address
- * in order to send the frame being processed.
- */
-
-int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f)
-{
- struct pico_eth dstmac;
- uint8_t dstmac_valid = 0;
- uint16_t proto = PICO_IDETH_IPV4;
-
-#ifdef PICO_SUPPORT_IPV6
- /* Step 1: If the frame has an IPv6 packet,
- * destination address is taken from the ND tables
- */
- if (IS_IPV6(f)) {
- if (pico_ethernet_ipv6_dst(f, &dstmac) < 0)
- {
- pico_ipv6_nd_postpone(f);
- return 0; /* I don't care if frame was actually postponed. If there is no room in the ND table, discard safely. */
- }
-
- dstmac_valid = 1;
- proto = PICO_IDETH_IPV6;
- }
- else
-#endif
-
- /* In case of broadcast (IPV4 only), dst mac is FF:FF:... */
- if (IS_BCAST(f) || destination_is_bcast(f))
- {
- memcpy(&dstmac, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
- dstmac_valid = 1;
- }
-
- /* In case of multicast, dst mac is translated from the group address */
- else if (destination_is_mcast(f)) {
- uint8_t pico_mcast_mac[6] = {
- 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
- };
- pico_ethernet_mcast_translate(f, pico_mcast_mac);
- memcpy(&dstmac, pico_mcast_mac, PICO_SIZE_ETH);
- dstmac_valid = 1;
- }
-
-#if (defined PICO_SUPPORT_IPV4)
- else {
- struct pico_eth *arp_get;
- arp_get = pico_arp_get(f);
- if (arp_get) {
- memcpy(&dstmac, arp_get, PICO_SIZE_ETH);
- dstmac_valid = 1;
- } else {
- /* At this point, ARP will discard the frame in any case.
- * It is safe to return without discarding.
- */
- pico_arp_postpone(f);
- return 0;
- /* Same case as for IPv6 ... */
- }
-
- }
-#endif
-
- /* This sets destination and source address, then pushes the packet to the device. */
- if (dstmac_valid) {
- struct pico_eth_hdr *hdr;
- hdr = (struct pico_eth_hdr *) f->datalink_hdr;
- if ((f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR))
- {
- f->start -= PICO_SIZE_ETHHDR;
- f->len += PICO_SIZE_ETHHDR;
- f->datalink_hdr = f->start;
- hdr = (struct pico_eth_hdr *) f->datalink_hdr;
- memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
- memcpy(hdr->daddr, &dstmac, PICO_SIZE_ETH);
- hdr->proto = proto;
- }
-
- if (pico_ethsend_local(f, hdr) || pico_ethsend_bcast(f) || pico_ethsend_dispatch(f)) {
- /* one of the above functions has delivered the frame accordingly. (returned != 0)
- * It is safe to directly return success.
- * */
- return 0;
- }
- }
-
- /* Failure: do not dequeue the frame, keep it for later. */
- return -1;
-}
-
-#endif /* PICO_SUPPORT_ETH */
-
-
void pico_store_network_origin(void *src, struct pico_frame *f)
{
#ifdef PICO_SUPPORT_IPV4
@@ -717,23 +371,73 @@ int pico_frame_dst_is_unicast(struct pico_frame *f)
else return 0;
}
+/*******************************************************************************
+ * DATALINK LAYER
+ ******************************************************************************/
-/* LOWEST LEVEL: interface towards devices. */
-/* Device driver will call this function which returns immediately.
- * Incoming packet will be processed later on in the dev loop.
- */
-extern int32_t pico_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len)
+int pico_datalink_receive(struct pico_frame *f)
+{
+ if (f->dev->eth) {
+ /* If device has stack with datalink-layer pass frame through it */
+ switch (f->dev->mode) {
+ #ifdef PICO_SUPPORT_802154
+ case LL_MODE_IEEE802154:
+ f->datalink_hdr = f->buffer;
+ return pico_enqueue(pico_proto_6lowpan_ll.q_in, f);
+ #endif
+ default:
+ #ifdef PICO_SUPPORT_ETH
+ f->datalink_hdr = f->buffer;
+ return pico_enqueue(pico_proto_ethernet.q_in,f);
+ #else
+ return -1;
+ #endif
+ }
+ } else {
+ /* If device handles raw IP-frames send it straight to network-layer */
+ f->net_hdr = f->buffer;
+ pico_network_receive(f);
+ }
+
+ return 0;
+}
+
+MOCKABLE int pico_datalink_send(struct pico_frame *f)
+{
+ if (f->dev->eth) {
+ switch (f->dev->mode) {
+ #ifdef PICO_SUPPORT_802154
+ case LL_MODE_IEEE802154:
+ return pico_enqueue(pico_proto_6lowpan.q_out, f);
+ #endif
+ default:
+ #ifdef PICO_SUPPORT_ETH
+ return pico_enqueue(pico_proto_ethernet.q_out, f);
+ #else
+ return -1;
+ #endif
+ }
+ } else {
+ /* non-ethernet: no post-processing needed */
+ return pico_sendto_dev(f);
+ }
+}
+
+/*******************************************************************************
+ * PHYSICAL LAYER
+ ******************************************************************************/
+
+struct pico_frame *pico_stack_recv_new_frame(struct pico_device *dev, uint8_t *buffer, uint32_t len)
{
struct pico_frame *f;
- int32_t ret;
if (len == 0)
- return -1;
+ return NULL;
f = pico_frame_alloc(len);
if (!f)
{
dbg("Cannot alloc incoming frame!\n");
- return -1;
+ return NULL;
}
/* Association to the device that just received the frame. */
@@ -750,11 +454,25 @@ extern int32_t pico_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_
}
memcpy(f->buffer, buffer, len);
+ return f;
+}
+
+/* LOWEST LEVEL: interface towards devices. */
+/* Device driver will call this function which returns immediately.
+ * Incoming packet will be processed later on in the dev loop.
+ */
+int32_t pico_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len)
+{
+ struct pico_frame *f = pico_stack_recv_new_frame (dev, buffer, len);
+ int32_t ret;
+
+ if (!f)
+ return -1;
+
ret = pico_enqueue(dev->q_in, f);
if (ret <= 0) {
pico_frame_discard(f);
}
-
return ret;
}
@@ -837,6 +555,7 @@ struct pico_timer_ref
{
pico_time expire;
uint32_t id;
+ uint32_t hash;
struct pico_timer *tmr;
};
@@ -891,7 +610,6 @@ static void pico_check_timers(void)
PICO_FREE(t);
}
- t = NULL;
heap_peek(Timers, &tref_unused);
tref = heap_first(Timers);
}
@@ -900,18 +618,44 @@ static void pico_check_timers(void)
void MOCKABLE pico_timer_cancel(uint32_t id)
{
uint32_t i;
- struct pico_timer_ref *tref = Timers->top;
+ struct pico_timer_ref *tref;
if (id == 0u)
return;
+
for (i = 1; i <= Timers->n; i++) {
- if (tref[i].id == id) {
- PICO_FREE(Timers->top[i].tmr);
- Timers->top[i].tmr = NULL;
+ tref = heap_get_element(Timers, i);
+ if (tref->id == id) {
+ if (tref->tmr)
+ {
+ PICO_FREE(tref->tmr);
+ tref->tmr = NULL;
+ tref->id = 0;
+ }
break;
}
}
}
+void pico_timer_cancel_hashed(uint32_t hash)
+{
+ uint32_t i;
+ struct pico_timer_ref *tref;
+ if (hash == 0u)
+ return;
+
+ for (i = 1; i <= Timers->n; i++) {
+ tref = heap_get_element(Timers, i);
+ if (tref->hash == hash) {
+ if (tref->tmr)
+ {
+ PICO_FREE(tref->tmr);
+ tref->tmr = NULL;
+ tref[i].id = 0;
+ }
+ }
+ }
+}
+
#define PROTO_DEF_NR 11
#define PROTO_DEF_AVG_NR 4
#define PROTO_DEF_SCORE 32
@@ -1076,26 +820,22 @@ void pico_stack_loop(void)
}
}
-MOCKABLE uint32_t pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg)
+static uint32_t
+pico_timer_ref_add(pico_time expire, struct pico_timer *t, uint32_t id, uint32_t hash)
{
- struct pico_timer *t = PICO_ZALLOC(sizeof(struct pico_timer));
struct pico_timer_ref tref;
- /* zero is guard for timers */
- if (tmr_id == 0u)
- tmr_id++;
+ tref.expire = PICO_TIME_MS() + expire;
+ tref.tmr = t;
+ tref.id = id;
+ tref.hash = hash;
- if (!t) {
+ if (heap_insert(Timers, &tref) < 0) {
+ dbg("Error: failed to insert timer(ID %u) into heap\n", id);
+ PICO_FREE(t);
pico_err = PICO_ERR_ENOMEM;
return 0;
}
-
- tref.expire = PICO_TIME_MS() + expire;
- t->arg = arg;
- t->timer = timer;
- tref.tmr = t;
- tref.id = tmr_id++;
- heap_insert(Timers, &tref);
if (Timers->n > PICO_MAX_TIMERS) {
dbg("Warning: I have %d timers\n", (int)Timers->n);
}
@@ -1103,8 +843,62 @@ MOCKABLE uint32_t pico_timer_add(pico_time expire, void (*timer)(pico_time, void
return tref.id;
}
-extern int pico_stack_init(void)
+static struct pico_timer *
+pico_timer_create(void (*timer)(pico_time, void *), void *arg)
{
+ struct pico_timer *t = PICO_ZALLOC(sizeof(struct pico_timer));
+
+ if (!t) {
+ pico_err = PICO_ERR_ENOMEM;
+ return NULL;
+ }
+
+ t->arg = arg;
+ t->timer = timer;
+
+ return t;
+}
+
+MOCKABLE uint32_t pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg)
+{
+ struct pico_timer *t = pico_timer_create(timer, arg);
+
+ /* zero is guard for timers */
+ if (tmr_id == 0u) {
+ tmr_id++;
+ }
+
+ if (!t)
+ return 0;
+
+ return pico_timer_ref_add(expire, t, tmr_id++, 0);
+}
+
+uint32_t pico_timer_add_hashed(pico_time expire, void (*timer)(pico_time, void *), void *arg, uint32_t hash)
+{
+ struct pico_timer *t = pico_timer_create(timer, arg);
+
+ /* zero is guard for timers */
+ if (tmr_id == 0u) {
+ tmr_id++;
+ }
+
+ if (!t)
+ return 0;
+
+ return pico_timer_ref_add(expire, t, tmr_id++, hash);
+} /* Static path count: 4 */
+
+extern int MOCKABLE pico_stack_init(void)
+{
+#ifdef PICO_SUPPORT_ETH
+ pico_protocol_init(&pico_proto_ethernet);
+#endif
+
+#ifdef PICO_SUPPORT_6LOWPAN
+ pico_protocol_init(&pico_proto_6lowpan);
+ pico_protocol_init(&pico_proto_6lowpan_ll);
+#endif
#ifdef PICO_SUPPORT_IPV4
pico_protocol_init(&pico_proto_ipv4);
@@ -1161,7 +955,10 @@ extern int pico_stack_init(void)
#ifdef PICO_SUPPORT_AODV
pico_aodv_init();
#endif
-
+#ifdef PICO_SUPPORT_6LOWPAN
+ if (pico_6lowpan_init())
+ return -1;
+#endif
pico_stack_tick();
pico_stack_tick();
pico_stack_tick();
diff --git a/ext/picotcp/stack/pico_tree.c b/ext/picotcp/stack/pico_tree.c
index 8af9845..8955696 100644
--- a/ext/picotcp/stack/pico_tree.c
+++ b/ext/picotcp/stack/pico_tree.c
@@ -1,6 +1,6 @@
/*********************************************************************
- PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
- See LICENSE and COPYING for usage.
+ PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
+ See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Author: Andrei Carp
*********************************************************************/
@@ -77,6 +77,7 @@ struct pico_tree_node *pico_tree_next(struct pico_tree_node *node)
{
if (!node)
return NULL;
+
if(IS_NOT_LEAF(node->rightChild))
{
node = node->rightChild;
@@ -127,35 +128,12 @@ void *pico_tree_insert(struct pico_tree *tree, void *key)
return pico_tree_insert_implementation(tree, key, USE_PICO_ZALLOC);
}
-void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator)
+static void pico_tree_insert_node(struct pico_tree *tree, struct pico_tree_node *insert)
{
- struct pico_tree_node *last_node = INIT_LEAF;
struct pico_tree_node *temp = tree->root;
- struct pico_tree_node *insert;
- void *LocalKey;
+ struct pico_tree_node *last_node = INIT_LEAF;
int result = 0;
- LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree, key) : NULL);
-
- /* if node already in, bail out */
- if(LocalKey) {
- return LocalKey;
- }
- else
- {
- if(allocator == USE_PICO_PAGE0_ZALLOC)
- insert = create_node(tree, key, USE_PICO_PAGE0_ZALLOC);
- else
- insert = create_node(tree, key, USE_PICO_ZALLOC);
-
- if(!insert)
- {
- pico_err = PICO_ERR_ENOMEM;
- /* to let the user know that it couldn't insert */
- return (void *)&LEAF;
- }
- }
-
/* search for the place to insert the new node */
while(IS_NOT_LEAF(temp))
{
@@ -176,6 +154,31 @@ void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t
else
last_node->rightChild = insert;
}
+}
+
+void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator)
+{
+ struct pico_tree_node *insert;
+ void *LocalKey;
+
+ LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree, key) : NULL);
+
+ /* if node already in, bail out */
+ if(LocalKey) {
+ pico_err = PICO_ERR_EEXIST;
+ return LocalKey;
+ }
+
+ insert = create_node(tree, key, allocator);
+
+ if(!insert)
+ {
+ pico_err = PICO_ERR_ENOMEM;
+ /* to let the user know that it couldn't insert */
+ return (void *)&LEAF;
+ }
+
+ pico_tree_insert_node(tree, insert);
/* fix colour issues */
fix_insert_collisions(tree, insert);
@@ -194,9 +197,7 @@ struct pico_tree_node *pico_tree_findNode(struct pico_tree *tree, void *key)
int result;
result = tree->compare(found->keyValue, key);
if(result == 0)
- {
return found;
- }
else if(result < 0)
found = found->rightChild;
else
@@ -209,22 +210,10 @@ void *pico_tree_findKey(struct pico_tree *tree, void *key)
{
struct pico_tree_node *found;
-
- found = tree->root;
- while(IS_NOT_LEAF(found))
- {
- int result;
-
- result = tree->compare(found->keyValue, key);
- if(result == 0)
- return found->keyValue;
- else if(result < 0)
- found = found->rightChild;
- else
- found = found->leftChild;
-
- }
- return NULL;
+ found = pico_tree_findNode(tree, key);
+ if (found == NULL)
+ return NULL;
+ return found->keyValue;
}
void *pico_tree_first(struct pico_tree *tree)
@@ -302,7 +291,7 @@ void *pico_tree_delete(struct pico_tree *tree, void *key)
static inline void if_nodecolor_black_fix_collisions(struct pico_tree *tree, struct pico_tree_node *temp, uint8_t nodeColor)
{
/* deleted node is black, this will mess up the black path property */
- if(nodeColor == BLACK)
+ if(nodeColor == BLACK)
fix_delete_collisions(tree, temp);
}
@@ -314,12 +303,13 @@ void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t
struct pico_tree_node *delete; /* keeps a copy of the node to be extracted */
if (!key)
return NULL;
+
delete = pico_tree_findNode(tree, key);
/* this key isn't in the tree, bail out */
- if(!delete)
+ if(!delete)
return NULL;
-
+
lkey = delete->keyValue;
nodeColor = pico_tree_delete_check_switch(tree, delete, &temp);
@@ -442,7 +432,7 @@ static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*n
node = GRANPA(node);
}
else if(temp->color == BLACK) {
- if(node == node->parent->leftChild) {
+ if(AM_I_LEFT_CHILD(node)) {
node = node->parent;
rotateToRight(tree, node);
}
diff --git a/ext/picotcp/test/Makefile b/ext/picotcp/test/Makefile
new file mode 100644
index 0000000..038d1e6
--- /dev/null
+++ b/ext/picotcp/test/Makefile
@@ -0,0 +1,10 @@
+CC=gcc
+CFLAGS=-ggdb -I../build/include/ -L../build/lib/ -lpicotcp -lvdeplug
+APPNAME=test_tftp_app_client
+
+.PHONY: clean
+all: test_tftp_app_client
+test_tftp_app_client: test_tftp_app_client.o
+ $(CC) -o test_tftp_app_client ../build/modules/pico_dev_vde.o $^ $(CFLAGS)
+clean:
+ rm -f ${APPNAME}.o
diff --git a/ext/picotcp/test/README.md b/ext/picotcp/test/README.md
new file mode 100644
index 0000000..0e284ff
--- /dev/null
+++ b/ext/picotcp/test/README.md
@@ -0,0 +1,6 @@
+To run these tests on your linux system, you will have to install these dependencies:
+* vde2
+* libvdeplug2-dev
+* libpcap0.8-dev
+
+This will allow you to compile the 'make test' and run the tests
diff --git a/ext/picotcp/test/autotest.sh b/ext/picotcp/test/autotest.sh
new file mode 100755
index 0000000..407b042
--- /dev/null
+++ b/ext/picotcp/test/autotest.sh
@@ -0,0 +1,360 @@
+#!/bin/bash
+
+TFTP_EXEC_DIR="$(pwd)/build/test"
+TFTP_WORK_DIR="${TFTP_EXEC_DIR}/tmp"
+TFTP_WORK_SUBDIR="${TFTP_WORK_DIR}/subdir"
+TFTP_WORK_FILE="test.img"
+
+
+
+function tftp_setup() {
+ dd if=/dev/urandom bs=1000 count=10 of=${1}/$TFTP_WORK_FILE
+}
+
+function tftp_cleanup() {
+ echo CLEANUP
+ pwd;ls
+ killall -wq picoapp.elf
+ rm -rf $TFTP_WORK_DIR
+ if [ $1 ]; then
+ exit $1
+ fi
+}
+
+if ! [ -x "$(command -v vde_switch)" ]; then
+ echo 'VDE Switch is not installed.' >&2
+fi
+
+if [ ! -e test/vde_sock_start_user.sh ]; then
+ echo "VDE SOCK START FILE NOT FOUND. NO VDE SETUP. EXITING"
+ exit 1
+else
+ echo "VDE SOCK START SCRIPT STARTED."
+ ./test/vde_sock_start_user.sh
+fi
+
+rm -f /tmp/pico-mem-report-*
+sleep 2
+ulimit -c unlimited
+killall -wq picoapp.elf
+killall -wq picoapp6.elf
+
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ 6LoWPAN PING 1HOP (1500B) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(build/test/picoapp6.elf -6 0,0,0) &
+pids="$! "
+sleep 1
+(build/test/picoapp6.elf -6 1,2,1 -a noop) &
+pids+="$! "
+sleep 1
+build/test/picoapp6.elf -6 2,1,0 -a ping,2aaa:abcd:0000:0000:0200:00aa:ab00:0001,1500,0,1 || exit 1
+#TODO roll out this check for all "daemon" processes
+for pid in $pids; do ps -o pid= -p $pid || exit 1; done # check whether daemon processes didn't die from e.g. ASAN
+killall -w picoapp6.elf -s SIGQUIT
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ 6LoWPAN UDP 1HOP (1400B) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+#TODO are these "daemon" processes that need to be killed, or are they intended to halt on their own, giving a status code?
+(build/test/picoapp6.elf -6 0,0,0) &
+sleep 1
+(build/test/picoapp6.elf -6 1,2,1 -a udpecho,::0,6667,) &
+sleep 1
+build/test/picoapp6.elf -6 2,1,0 -a udpclient,2aaa:abcd:0000:0000:0200:00aa:ab00:0001,6667,6667,1400,10,1, || exit 1
+killall -w picoapp6.elf -s SIGQUIT
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ MULTICAST6 TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic1,/tmp/pic0.ctl,aaaa::2,ffff::, -a mcastreceive_ipv6,aaaa::2,ff00::e007:707,6667,6667,) &
+(./build/test/picoapp6.elf --vde pic2,/tmp/pic0.ctl,aaaa::3,ffff::, -a mcastreceive_ipv6,aaaa::3,ff00::e007:707,6667,6667,) &
+(./build/test/picoapp6.elf --vde pic3,/tmp/pic0.ctl,aaaa::4,ffff::, -a mcastreceive_ipv6,aaaa::4,ff00::e007:707,6667,6667,) &
+sleep 2
+ ./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::, -a mcastsend_ipv6,aaaa::1,ff00::e007:707,6667,6667,|| exit 1
+killall -w picoapp6.elf -s SIGQUIT
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ PING6 LOCALHOST TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+./build/test/picoapp6.elf --loop -a ping,::1,,,, || exit 1
+killall -w picoapp6.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ PING6 TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::,,,,) &
+./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::2,ffff::,,, -a ping,aaaa::1,,,, || exit 1
+killall -w picoapp6.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ PING6 TEST (aborted in 4 seconds...) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::,,,,) &
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::2,ffff::,,, -a ping,aaaa::1,64,4,,) &
+sleep 7
+killall -w picoapp6.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TCP6 TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::,,, -a tcpbench,r,6667,,) &
+./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::2,ffff::,,, -a tcpbench,t,aaaa::1,6667,, || exit 1
+killall -w picoapp6.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TCP6 TEST (with 2% packet loss on both directions) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::,,2,2, -a tcpbench,r,6667,,) &
+./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::2,ffff::,,, -a tcpbench,t,aaaa::1,6667,, || exit 1
+killall -w picoapp6.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TCP6 TEST (nagle) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::,,, -a tcpbench,r,6667,n,) &
+./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::2,ffff::,,, -a tcpbench,t,aaaa::1,6667,n, || exit 1
+killall -w picoapp6.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ UDP6 TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::,,, -a udpecho,::0,6667,) &
+pids="$! "
+./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::2,ffff::,,, -a udpclient,aaaa::1,6667,6667,1400,100,10, || exit 1
+wait $pids || exit 1
+killall -w picoapp6.elf
+
+echo
+echo
+echo
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ IPV6 FWD TCP TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic1.ctl,2001:aabb::2,ffff:ffff::,2001:aabb::ff,, -a tcpbench,r,6667,,) &
+(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,2001:aaaa::ff,ffff:ffff::,,, --vde pic1,/tmp/pic1.ctl,2001:aabb::ff,ffff:ffff::,,, -a noop,) &
+./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,2001:aaaa::1,ffff:ffff::,2001:aaaa::ff,, -a tcpbench,t,2001:aabb::2,6667,, || exit 1
+sleep 2
+killall -w picoapp6.elf
+
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ MULTICAST TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic1:/tmp/pic0.ctl:10.40.0.3:255.255.0.0: -a mcastreceive:10.40.0.3:224.7.7.7:6667:6667:) &
+(./build/test/picoapp.elf --vde pic2:/tmp/pic0.ctl:10.40.0.4:255.255.0.0: -a mcastreceive:10.40.0.4:224.7.7.7:6667:6667:) &
+(./build/test/picoapp.elf --vde pic3:/tmp/pic0.ctl:10.40.0.5:255.255.0.0: -a mcastreceive:10.40.0.5:224.7.7.7:6667:6667:) &
+sleep 2
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.0.0: -a mcastsend:10.40.0.2:224.7.7.7:6667:6667: || exit 1
+killall -w picoapp.elf
+
+echo
+echo
+echo
+echo
+echo
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ IPV4 tests! ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ PING LOCALHOST TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+./build/test/picoapp.elf --loop -a ping:127.0.0.1:::: || exit 1
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ PING TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0:::) &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a ping:10.40.0.8:::: || exit 1
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ PING TEST -- Aborted in 4 seconds ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0:::) &
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a ping:10.40.0.8:64:4::) &
+sleep 7
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TCP TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0:::: -a tcpbench:r:6667::) &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a tcpbench:t:10.40.0.8:6667:: || exit 1
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TCP TEST (with global route) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0:::: --vde pic1:/tmp/pic1.ctl:10.50.0.10:255.255.0.0:10.50.0.1: -a tcpbench:r:6667::) &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a tcpbench:t:10.40.0.8:6667:: || exit 1
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TCP TEST (with 2% packet loss on both directions) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0::2:2: -a tcpbench:r:6667::) &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a tcpbench:t:10.40.0.8:6667:: || exit 1
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TCP TEST (nagle) ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0::: -a tcpbench:r:6667:n:) &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a tcpbench:t:10.40.0.8:6667:n: || exit 1
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ UDP TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0::: -a udpecho:10.40.0.8:6667:) &
+pids="$! "
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a udpclient:10.40.0.8:6667:6667:1400:100:10: || exit 1
+wait $pids || exit 1
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ UDP TEST with fragmentation ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.8:255.255.0.0::: -a udpecho:10.40.0.8:6667:) &
+pids="$! "
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0::: -a udpclient:10.40.0.8:6667:6667:4500:100:10: || exit 1
+wait $pids || exit 1
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ NAT TCP TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.10:255.255.0.0::: --vde pic1:/tmp/pic1.ctl:10.50.0.10:255.255.0.0: -a natbox:10.50.0.10) &
+sleep 2
+(./build/test/picoapp.elf --vde pic0:/tmp/pic1.ctl:10.50.0.8:255.255.0.0::: -a tcpbench:r:6667:) &
+sleep 2
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0:10.40.0.10::: -a tcpbench:t:10.50.0.8:6667: || exit 1
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ NAT UDP TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.10:255.255.0.0::: --vde pic1:/tmp/pic1.ctl:10.50.0.10:255.255.0.0::: -a natbox:10.50.0.10) &
+(./build/test/picoapp.elf --vde pic0:/tmp/pic1.ctl:10.50.0.8:255.255.0.0::: -a udpecho:10.50.0.8:6667:) &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.9:255.255.0.0:10.40.0.10::: -a udpclient:10.50.0.8:6667:6667:1400:100:10: || exit 1
+#sometimes udpecho finishes before reaching wait %2
+#wait %2
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ MULTICAST TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic1:/tmp/pic0.ctl:10.40.0.3:255.255.0.0::: -a mcastreceive:10.40.0.3:224.7.7.7:6667:6667:) &
+(./build/test/picoapp.elf --vde pic2:/tmp/pic0.ctl:10.40.0.4:255.255.0.0::: -a mcastreceive:10.40.0.4:224.7.7.7:6667:6667:) &
+(./build/test/picoapp.elf --vde pic3:/tmp/pic0.ctl:10.40.0.5:255.255.0.0::: -a mcastreceive:10.40.0.5:224.7.7.7:6667:6667:) &
+sleep 2
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.0.0::: -a mcastsend:10.40.0.2:224.7.7.7:6667:6667: || exit 1
+killall -w picoapp.elf
+
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ DHCP TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.1:255.255.0.0::: -a dhcpserver:pic0:10.40.0.1:255.255.255.0:64:128:) &
+./build/test/picoapp.elf --barevde pic0:/tmp/pic0.ctl: -a dhcpclient:pic0 || exit 1
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ DHCP DUAL TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.0.0::: -a dhcpserver:pic0:10.40.0.2:255.255.255.0:64:128:) &
+(./build/test/picoapp.elf --vde pic1:/tmp/pic1.ctl:10.50.0.2:255.255.0.0::: -a dhcpserver:pic1:10.50.0.2:255.255.255.0:64:128:) &
+./build/test/picoapp.elf --barevde pic0:/tmp/pic0.ctl: --barevde pic1:/tmp/pic1.ctl: -a dhcpclient:pic0:pic1: || exit 1
+killall -w picoapp.elf
+
+#TO DO: the ping address 169.254.22.5 is hardcoded in the slaacv4 test. Nice to pass that by parameter
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ SLAACV4 TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:169.254.22.5:255.255.0.0:::) &
+./build/test/picoapp.elf --barevde pic0:/tmp/pic0.ctl: -a slaacv4:pic0 || exit 1
+killall -w picoapp.elf
+
+
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.0.0:10.40.0.1::: -a udpdnsclient:www.google.be:173.194.67.94:: &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.0.0:10.40.0.1::: -a udpdnsclient:ipv6.google.be:doesntmatter:ipv6: &
+./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.2:255.255.0.0:10.50.0.1::: -a sntp:0.europe.pool.ntp.org &
+sleep 20
+killall -w picoapp.elf
+
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ MDNS TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+#retrieve a local mdns host name from the host
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.2:255.255.255.0:10.50.0.1: --app mdns:hostfoo.local:hostbar.local:) &
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.3:255.255.255.0:10.50.0.1: --app mdns:hostbar.local:hostfoo.local:) &
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.2:255.255.255.0:10.50.0.1: --app mdns:hostfoobar.local:nonexisting.local:) &
+sleep 10
+killall -w picoapp.elf
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ DNS_SD TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+#register a service
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.2:255.255.255.0:10.50.0.1: --app dns_sd:host.local:WebServer) &
+(./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.3:255.255.255.0:10.50.0.1: --app dns_sd:host.local:WebServer) &
+sleep 30
+killall -w picoapp.elf
+
+sleep 1
+sync
+
+
+# TFTP TEST BEGINS...
+
+if [ ! -d $TFTP_WORK_DIR ]; then
+ mkdir $TFTP_WORK_DIR || exit 1
+fi
+if [ ! -d ${TFTP_WORK_SUBDIR}/server ]; then
+ mkdir $TFTP_WORK_SUBDIR || exit 1
+fi
+
+pushd $TFTP_WORK_DIR
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TFTP GET TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+tftp_setup $TFTP_WORK_DIR
+(${TFTP_EXEC_DIR}/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.2:255.255.255.0:10.50.0.1: --app tftp:S:) &
+cd $TFTP_WORK_SUBDIR
+sleep 2
+${TFTP_EXEC_DIR}/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.3:255.255.255.0:10.50.0.1: --app tftp:R:${TFTP_WORK_FILE}:10.50.0.2: || tftp_cleanup 1
+sleep 3
+killall -w picoapp.elf
+
+sleep 1
+
+rm $TFTP_WORK_FILE
+
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~ TFTP PUT TEST ~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+(${TFTP_EXEC_DIR}/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.2:255.255.255.0:10.50.0.1: --app tftp:S:) &
+cd $TFTP_WORK_DIR
+tftp_setup $TFTP_WORK_DIR
+sleep 2
+${TFTP_EXEC_DIR}/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.50.0.3:255.255.255.0:10.50.0.1: --app tftp:T:${TFTP_WORK_FILE}:10.50.0.2: || tftp_cleanup 1
+sleep 3
+
+tftp_cleanup
+popd
+# TFTP TEST ENDS.
+
+MAXMEM=`cat /tmp/pico-mem-report-* | sort -r -n |head -1`
+echo
+echo
+echo
+echo "MAX memory used: $MAXMEM"
+rm -f /tmp/pico-mem-report-*
+
+./test/vde_sock_start_user.sh stop
+echo "SUCCESS!"
diff --git a/ext/picotcp/test/coverage.sh b/ext/picotcp/test/coverage.sh
new file mode 100755
index 0000000..f326bc7
--- /dev/null
+++ b/ext/picotcp/test/coverage.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+./test/units.sh || exit 1
+./test/autotest.sh || exit 2
+exit 0
diff --git a/ext/picotcp/test/dummy.c b/ext/picotcp/test/dummy.c
new file mode 100644
index 0000000..7420581
--- /dev/null
+++ b/ext/picotcp/test/dummy.c
@@ -0,0 +1,12 @@
+#include "pico_stack.h"
+
+#if defined(PICO_SUPPORT_RTOS) || defined (PICO_SUPPORT_PTHREAD)
+volatile uint32_t pico_ms_tick;
+#endif
+
+int main(void)
+{
+ pico_stack_init();
+ pico_stack_tick();
+ return 0;
+}
diff --git a/ext/picotcp/test/examples/Makefile b/ext/picotcp/test/examples/Makefile
new file mode 100644
index 0000000..3434891
--- /dev/null
+++ b/ext/picotcp/test/examples/Makefile
@@ -0,0 +1,36 @@
+PREFIX?=../../build
+CFLAGS+=-I../../include/ -I../../modules -I../../build/include -I . -ggdb
+
+
+$(PREFIX)/examples/%.o: %.c
+ @mkdir -p $(PREFIX)/examples
+ @echo -e "\t[CC] $@"
+ @$(CC) -c $(CFLAGS) -o $@ $<
+
+OBJS:= \
+$(PREFIX)/examples/dhcp_client.o \
+$(PREFIX)/examples/dhcp_server.o \
+$(PREFIX)/examples/dns_sd.o \
+$(PREFIX)/examples/dnsclient.o \
+$(PREFIX)/examples/mdns.o \
+$(PREFIX)/examples/multicast_recv.o \
+$(PREFIX)/examples/multicast_ip6_recv.o \
+$(PREFIX)/examples/multicast_send.o \
+$(PREFIX)/examples/multicast_ip6_send.o \
+$(PREFIX)/examples/natbox.o \
+$(PREFIX)/examples/noop.o \
+$(PREFIX)/examples/ping.o \
+$(PREFIX)/examples/slaacv4.o \
+$(PREFIX)/examples/sntp.o \
+$(PREFIX)/examples/tcpbench.o \
+$(PREFIX)/examples/tcpclient.o \
+$(PREFIX)/examples/tcpecho.o \
+$(PREFIX)/examples/tftp.o \
+$(PREFIX)/examples/udp_client.o \
+$(PREFIX)/examples/udp_echo.o \
+$(PREFIX)/examples/udpnat.o \
+$(PREFIX)/examples/udp_sendto_test.o \
+$(PREFIX)/examples/iperfc.o \
+
+
+all: $(OBJS)
diff --git a/ext/picotcp/test/examples/dhcp_client.c b/ext/picotcp/test/examples/dhcp_client.c
new file mode 100644
index 0000000..bb0ee20
--- /dev/null
+++ b/ext/picotcp/test/examples/dhcp_client.c
@@ -0,0 +1,114 @@
+#include "utils.h"
+#include
+#include
+#include
+#include
+#include
+/*** START DHCP Client ***/
+#ifdef PICO_SUPPORT_DHCPC
+
+/* This must stay global, its lifetime is the same as the dhcp negotiation */
+uint32_t dhcpclient_xid;
+
+
+static uint8_t dhcpclient_devices = 0;
+
+void ping_callback_dhcpclient(struct pico_icmp4_stats *s)
+{
+ char host[30] = { };
+
+ pico_ipv4_to_string(host, s->dst.addr);
+ if (s->err == 0) {
+ dbg("DHCP client: %lu bytes from %s: icmp_req=%lu ttl=64 time=%lu ms\n",
+ s->size, host, s->seq, (long unsigned int)s->time);
+ if (s->seq >= 3) {
+ dbg("DHCP client: TEST SUCCESS!\n");
+ if (--dhcpclient_devices <= 0)
+ exit(0);
+ }
+ } else {
+ dbg("DHCP client: ping %lu to %s error %d\n", s->seq, host, s->err);
+ dbg("DHCP client: TEST FAILED!\n");
+ exit(1);
+ }
+}
+
+void callback_dhcpclient(void *arg, int code)
+{
+ struct pico_ip4 address = ZERO_IP4, gateway = ZERO_IP4;
+ char s_address[16] = { }, s_gateway[16] = { };
+
+ printf("DHCP client: callback happened with code %d!\n", code);
+ if (code == PICO_DHCP_SUCCESS) {
+ address = pico_dhcp_get_address(arg);
+ gateway = pico_dhcp_get_gateway(arg);
+ pico_ipv4_to_string(s_address, address.addr);
+ pico_ipv4_to_string(s_gateway, gateway.addr);
+ printf("DHCP client: got IP %s assigned with cli %p\n", s_address, arg);
+#ifdef PICO_SUPPORT_PING
+ pico_icmp4_ping(s_gateway, 3, 1000, 5000, 32, ping_callback_dhcpclient);
+ /* optional test to check routing when links get added and deleted */
+ /* do {
+ char *new_arg = NULL, *p = NULL;
+ new_arg = calloc(1, strlen(s_address) + strlen(":224.7.7.7:6667:6667") + 1);
+ p = strcat(new_arg, s_address);
+ p = strcat(p + strlen(s_address), ":224.7.7.7:6667:6667");
+ app_mcastsend(new_arg);
+ } while (0);
+ */
+#endif
+ }
+}
+
+void app_dhcp_client(char *arg)
+{
+ char *sdev = NULL;
+ char *nxt = arg;
+ struct pico_device *dev = NULL;
+
+ if (!nxt)
+ goto out;
+
+ while (nxt) {
+ if (nxt) {
+ nxt = cpy_arg(&sdev, nxt);
+ if(!sdev) {
+ goto out;
+ }
+ }
+
+ dev = pico_get_device(sdev);
+ if(dev == NULL) {
+ if (sdev)
+ free(sdev);
+
+ printf("%s: error getting device %s: %s\n", __FUNCTION__, dev->name, strerror(pico_err));
+ exit(255);
+ }
+
+ printf("Starting negotiation\n");
+
+ if (pico_dhcp_initiate_negotiation(dev, &callback_dhcpclient, &dhcpclient_xid) < 0) {
+ printf("%s: error initiating negotiation: %s\n", __FUNCTION__, strerror(pico_err));
+ if (sdev)
+ free(sdev);
+
+ exit(255);
+ }
+
+ if (sdev)
+ free(sdev);
+
+ dhcpclient_devices++;
+ }
+ return;
+
+out:
+ fprintf(stderr, "dhcpclient expects the following format: dhcpclient:dev_name:[dev_name]\n");
+ if (sdev)
+ free(sdev);
+
+ exit(255);
+}
+#endif
+/*** END DHCP Client ***/
diff --git a/ext/picotcp/test/examples/dhcp_server.c b/ext/picotcp/test/examples/dhcp_server.c
new file mode 100644
index 0000000..63ff8fe
--- /dev/null
+++ b/ext/picotcp/test/examples/dhcp_server.c
@@ -0,0 +1,99 @@
+#include "utils.h"
+#include
+#include
+#include
+
+/*** START DHCP Server ***/
+#ifdef PICO_SUPPORT_DHCPD
+/* ./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.1:255.255.0.0: -a dhcpserver:pic0:10.40.0.1:255.255.255.0:64:128
+ * ./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.10:255.255.255.0: --vde pic1:/tmp/pic1.ctl:10.50.0.10:255.255.255.0: \
+ * -a dhcpserver:pic0:10.40.0.10:255.255.255.0:64:128:pic1:10.50.0.10:255.255.255.0:64:128
+ */
+void app_dhcp_server(char *arg)
+{
+ struct pico_device *dev = NULL;
+ struct pico_dhcp_server_setting s = {
+ 0
+ };
+ int pool_start = 0, pool_end = 0;
+ char *s_name = NULL, *s_addr = NULL, *s_netm = NULL, *s_pool_start = NULL, *s_pool_end = NULL;
+ char *nxt = arg;
+
+ if (!nxt)
+ goto out;
+
+ while (nxt) {
+ if (nxt) {
+ nxt = cpy_arg(&s_name, nxt);
+ if (!s_name) {
+ goto out;
+ }
+ } else {
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&s_addr, nxt);
+ if (s_addr) {
+ pico_string_to_ipv4(s_addr, &s.server_ip.addr);
+ } else {
+ goto out;
+ }
+ } else {
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&s_netm, nxt);
+ if (s_netm) {
+ pico_string_to_ipv4(s_netm, &s.netmask.addr);
+ } else {
+ goto out;
+ }
+ } else {
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&s_pool_start, nxt);
+ if (s_pool_start && atoi(s_pool_start)) {
+ pool_start = atoi(s_pool_start);
+ } else {
+ goto out;
+ }
+ } else {
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&s_pool_end, nxt);
+ if (s_pool_end && atoi(s_pool_end)) {
+ pool_end = atoi(s_pool_end);
+ } else {
+ goto out;
+ }
+ } else {
+ goto out;
+ }
+
+ dev = (struct pico_device *)pico_get_device(s_name);
+ if (dev == NULL) {
+ fprintf(stderr, "No device with name %s found\n", s_name);
+ exit(255);
+ }
+
+ s.dev = dev;
+ s.pool_start = (s.server_ip.addr & s.netmask.addr) | long_be(pool_start);
+ s.pool_end = (s.server_ip.addr & s.netmask.addr) | long_be(pool_end);
+
+ pico_dhcp_server_initiate(&s);
+ }
+ return;
+
+out:
+ fprintf(stderr, "dhcpserver expects the following format: dhcpserver:dev_name:dev_addr:dev_netm:pool_start:pool_end\n");
+ exit(255);
+
+}
+#endif
+/*** END DHCP Server ***/
diff --git a/ext/picotcp/test/examples/dns_sd.c b/ext/picotcp/test/examples/dns_sd.c
new file mode 100644
index 0000000..dd2e96f
--- /dev/null
+++ b/ext/picotcp/test/examples/dns_sd.c
@@ -0,0 +1,112 @@
+#include "utils.h"
+#include
+#include
+#include
+#include
+#include
+
+/*** START DNS_SD ***/
+#ifdef PICO_SUPPORT_DNS_SD
+
+#define TTL 30
+#define SECONDS 10
+
+static int fully_initialized = 0;
+static char *service_name = NULL;
+
+void dns_sd_claimed_callback( pico_mdns_rtree *tree,
+ char *str,
+ void *arg )
+{
+ printf("DONE - Registering DNS-SD Service\n");
+
+ IGNORE_PARAMETER(tree);
+ IGNORE_PARAMETER(str);
+ IGNORE_PARAMETER(arg);
+}
+
+void dns_sd_init_callback( pico_mdns_rtree *tree,
+ char *str,
+ void *arg )
+{
+ PICO_DNS_SD_KV_VECTOR_DECLARE(key_value_pair_vector);
+
+ IGNORE_PARAMETER(str);
+ IGNORE_PARAMETER(arg);
+ IGNORE_PARAMETER(tree);
+
+ pico_dns_sd_kv_vector_add(&key_value_pair_vector, "key", "value");
+
+ printf("DONE - Initialising DNS Service Discovery module.\n");
+
+ if (pico_dns_sd_register_service(service_name,
+ "_http._tcp", 80,
+ &key_value_pair_vector,
+ TTL, dns_sd_claimed_callback, NULL) < 0) {
+ printf("Registering service failed!\n");
+ }
+
+ fully_initialized = 1;
+}
+
+void app_dns_sd(char *arg, struct pico_ip4 address)
+{
+ char *hostname;
+ char *nxt = arg;
+ uint64_t starttime = 0;
+ int once = 0;
+
+ if (!nxt) {
+ exit(255);
+ }
+
+ nxt = cpy_arg(&hostname, nxt);
+ if(!hostname) {
+ exit(255);
+ }
+
+ if(!nxt) {
+ printf("Not enough args supplied!\n");
+ exit(255);
+ }
+
+ nxt = cpy_arg(&service_name, nxt);
+ if(!service_name) {
+ exit(255);
+ }
+
+ printf("\nStarting DNS Service Discovery module...\n");
+ if (pico_dns_sd_init(hostname, address, &dns_sd_init_callback, NULL) != 0) {
+ printf("Initialisation returned with Error!\n");
+ exit(255);
+ }
+
+ printf("\nTry reinitialising DNS-SD\n");
+ if (pico_dns_sd_init(hostname, address, &dns_sd_init_callback, NULL)) {
+ printf("Initialisation returned with Error!\n");
+ exit(255);
+ }
+
+ printf("DONE - Re-initialising DNS-SD module.\n");
+
+ starttime = PICO_TIME_MS();
+ printf("Starting time: %d\n", starttime);
+
+ while(1) {
+ pico_stack_tick();
+ usleep(2000);
+
+ if (((PICO_TIME_MS() - starttime) > SECONDS * 1000) && fully_initialized && !once) {
+ printf("\nTry reinitialising DNS-SD (a second time)\n");
+ if (pico_dns_sd_init(hostname, address, &dns_sd_init_callback, NULL)) {
+ printf("Initialisation returned with Error!\n");
+ exit(255);
+ }
+ once = 1;
+ printf("DONE - Re-initialising mDNS module. (a second time)\n");
+ }
+ }
+}
+
+#endif
+/*** END DNS_SD ***/
diff --git a/ext/picotcp/test/examples/dnsclient.c b/ext/picotcp/test/examples/dnsclient.c
new file mode 100644
index 0000000..0a1d276
--- /dev/null
+++ b/ext/picotcp/test/examples/dnsclient.c
@@ -0,0 +1,120 @@
+#include
+#include
+#include
+#include
+#include "utils.h"
+extern int IPV6_MODE;
+
+/*** START UDP DNS CLIENT ***/
+/*
+ ./test/vde_sock_start.sh
+ echo 1 > /proc/sys/net/ipv4/ip_forward
+ iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
+ iptables -A FORWARD -i pic0 -o wlan0 -j ACCEPT
+ iptables -A FORWARD -i wlan0 -o pic0 -j ACCEPT
+ ./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.0.0:10.40.0.1: -a udpdnsclient:www.google.be:173.194.67.94
+ */
+void cb_udpdnsclient_getaddr(char *ip, void *arg)
+{
+ uint8_t *id = (uint8_t *) arg;
+
+ if (!ip) {
+ picoapp_dbg("%s: ERROR occured! (id: %u)\n", __FUNCTION__, *id);
+ return;
+ }
+
+ picoapp_dbg("%s: ip %s (id: %u)\n", __FUNCTION__, ip, *id);
+ if (arg)
+ PICO_FREE(arg);
+}
+
+void cb_udpdnsclient_getname(char *name, void *arg)
+{
+ uint8_t *id = (uint8_t *) arg;
+
+ if (!name) {
+ picoapp_dbg("%s: ERROR occured! (id: %u)\n", __FUNCTION__, *id);
+ return;
+ }
+
+ picoapp_dbg("%s: name %s (id: %u)\n", __FUNCTION__, name, *id);
+ if (arg)
+ PICO_FREE(arg);
+}
+
+void app_udpdnsclient(char *arg)
+{
+ struct pico_ip4 nameserver;
+ char *dname, *daddr;
+ char *nxt;
+ char *ipver;
+ int v = 4;
+ uint8_t *getaddr_id, *getname_id, *getaddr6_id, *getname6_id;
+
+ nxt = cpy_arg(&dname, arg);
+ if (!dname || !nxt) {
+ picoapp_dbg(" udpdnsclient expects the following format: udpdnsclient:dest_name:dest_ip:[ipv6]\n");
+ exit(255);
+ }
+
+ nxt = cpy_arg(&daddr, nxt);
+ if (!daddr || !nxt) {
+ picoapp_dbg(" udpdnsclient expects the following format: udpdnsclient:dest_name:dest_ip:[ipv6]\n");
+ exit(255);
+ }
+
+ nxt = cpy_arg(&ipver, nxt);
+ if (!ipver || strcmp("ipv6", ipver) != 0)
+ v = 4;
+ else
+ v = 6;
+
+ picoapp_dbg("UDP DNS client started.\n");
+
+ picoapp_dbg("----- Deleting non existant nameserver -----\n");
+ pico_string_to_ipv4("127.0.0.1", &nameserver.addr);
+ pico_dns_client_nameserver(&nameserver, PICO_DNS_NS_DEL);
+ picoapp_dbg("----- Adding 8.8.8.8 nameserver -----\n");
+ pico_string_to_ipv4("8.8.8.8", &nameserver.addr);
+ pico_dns_client_nameserver(&nameserver, PICO_DNS_NS_ADD);
+ picoapp_dbg("----- Deleting 8.8.8.8 nameserver -----\n");
+ pico_string_to_ipv4("8.8.8.8", &nameserver.addr);
+ pico_dns_client_nameserver(&nameserver, PICO_DNS_NS_DEL);
+ picoapp_dbg("----- Adding 8.8.8.8 nameserver -----\n");
+ pico_string_to_ipv4("8.8.8.8", &nameserver.addr);
+ pico_dns_client_nameserver(&nameserver, PICO_DNS_NS_ADD);
+ picoapp_dbg("----- Adding 8.8.4.4 nameserver -----\n");
+ pico_string_to_ipv4("8.8.4.4", &nameserver.addr);
+ pico_dns_client_nameserver(&nameserver, PICO_DNS_NS_ADD);
+ if (!IPV6_MODE) {
+ if (v == 4) {
+ picoapp_dbg("Mode: IPv4\n");
+ getaddr_id = calloc(1, sizeof(uint8_t));
+ *getaddr_id = 1;
+ picoapp_dbg(">>>>> DNS GET ADDR OF %s\n", dname);
+ pico_dns_client_getaddr(dname, &cb_udpdnsclient_getaddr, getaddr_id);
+
+ getname_id = calloc(1, sizeof(uint8_t));
+ *getname_id = 2;
+ picoapp_dbg(">>>>> DNS GET NAME OF %s\n", daddr);
+ pico_dns_client_getname(daddr, &cb_udpdnsclient_getname, getname_id);
+ return;
+ }
+
+ picoapp_dbg("Mode: IPv6\n");
+
+#ifdef PICO_SUPPORT_IPV6
+ getaddr6_id = calloc(1, sizeof(uint8_t));
+ *getaddr6_id = 3;
+ picoapp_dbg(">>>>> DNS GET ADDR6 OF %s\n", dname);
+ pico_dns_client_getaddr6(dname, &cb_udpdnsclient_getaddr, getaddr6_id);
+ getname6_id = calloc(1, sizeof(uint8_t));
+ *getname6_id = 4;
+ picoapp_dbg(">>>>> DNS GET NAME OF ipv6 addr 2a00:1450:400c:c06::64\n");
+ pico_dns_client_getname6("2a00:1450:400c:c06::64", &cb_udpdnsclient_getname, getname6_id);
+#endif
+ }
+
+ return;
+}
+/*** END UDP DNS CLIENT ***/
diff --git a/ext/picotcp/test/examples/iperfc.c b/ext/picotcp/test/examples/iperfc.c
new file mode 100644
index 0000000..fbe7e67
--- /dev/null
+++ b/ext/picotcp/test/examples/iperfc.c
@@ -0,0 +1,142 @@
+#include
+#include
+#include
+#include "pico_ipv6.h"
+#include "pico_stack.h"
+#include "pico_socket.h"
+#include "utils.h"
+
+#define DURATION 30
+
+struct iperf_hdr {
+ int32_t flags; /* 0 */
+ int32_t numThreads; /* 1 */
+ int32_t mPort; /* 5001 */
+ int32_t bufferlen; /* 0 */
+ int32_t mWinBand; /* 0 */
+ int32_t mAmount; /* 0xfffffc18 */
+};
+
+#define IPERF_PORT 5001
+#define MTU 1444
+#define SEND_BUF_SIZ (1024 * 2048)
+
+char *cpy_arg(char **dst, char *str);
+extern int IPV6_MODE;
+
+static pico_time deadline;
+
+static void panic(void)
+{
+ for(;; ) ;
+}
+
+static char buf[MTU] = {};
+
+static void buf_paint(void)
+{
+ char paint[11] = "0123456789";
+ int i;
+ for (i = 0; i < MTU; i++) {
+ buf[i] = paint[i % 10];
+ }
+}
+
+static void send_hdr(struct pico_socket *s)
+{
+ struct iperf_hdr hdr = {};
+ hdr.numThreads = long_be(1);
+ hdr.mPort = long_be(5001);
+ hdr.mAmount = long_be(0xfffffc18);
+ pico_socket_write(s, &hdr, sizeof(hdr));
+ deadline = PICO_TIME_MS() + DURATION * 1000;
+}
+
+static void iperf_cb(uint16_t ev, struct pico_socket *s)
+{
+ int r;
+ static int end = 0;
+ if (ev & PICO_SOCK_EV_CONN) {
+ send_hdr(s);
+ return;
+ }
+
+ if ((!end) && (ev & PICO_SOCK_EV_WR)) {
+ if (PICO_TIME_MS() > deadline) {
+ pico_socket_close(s);
+ if (!pico_timer_add(2000, deferred_exit, NULL)) {
+ printf("Failed to start exit timer, exiting now\n");
+ exit(1);
+ }
+ end++;
+ }
+
+ pico_socket_write(s, buf, MTU);
+ }
+
+ if (!(end) && (ev & (PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE))) {
+ if (!pico_timer_add(2000, deferred_exit, NULL)) {
+ printf("Failed to start exit timer, exiting now\n");
+ exit(1);
+ }
+ end++;
+ }
+}
+
+static void iperfc_socket_setup(union pico_address *addr, uint16_t family)
+{
+ int yes = 1;
+ uint16_t send_port = 0;
+ struct pico_socket *s = NULL;
+ uint32_t bufsize = SEND_BUF_SIZ;
+ send_port = short_be(5001);
+ s = pico_socket_open(family, PICO_PROTO_TCP, &iperf_cb);
+ pico_socket_setoption(s, PICO_SOCKET_OPT_SNDBUF, &bufsize);
+ pico_socket_connect(s, addr, send_port);
+}
+
+void app_iperfc(char *arg)
+{
+ struct pico_ip4 my_eth_addr, netmask;
+ struct pico_device *pico_dev_eth;
+ char *daddr = NULL, *dport = NULL;
+ char *nxt = arg;
+ uint16_t send_port = 0, listen_port = short_be(5001);
+ int i = 0, ret = 0, yes = 1;
+ struct pico_socket *s = NULL;
+ uint16_t family = PICO_PROTO_IPV4;
+ union pico_address dst = {
+ .ip4 = {0}, .ip6 = {{0}}
+ };
+ union pico_address inaddr_any = {
+ .ip4 = {0}, .ip6 = {{0}}
+ };
+
+ /* start of argument parsing */
+ if (nxt) {
+ nxt = cpy_arg(&daddr, arg);
+ if (daddr) {
+ if (!IPV6_MODE)
+ pico_string_to_ipv4(daddr, &dst.ip4.addr);
+
+ #ifdef PICO_SUPPORT_IPV6
+ else {
+ pico_string_to_ipv6(daddr, dst.ip6.addr);
+ family = PICO_PROTO_IPV6;
+ }
+ #endif
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing dest_addr */
+ goto out;
+ }
+
+ iperfc_socket_setup(&dst, family);
+ return;
+out:
+ dbg("Error parsing options!\n");
+ exit(1);
+}
+
diff --git a/ext/picotcp/test/examples/mdns.c b/ext/picotcp/test/examples/mdns.c
new file mode 100644
index 0000000..853448f
--- /dev/null
+++ b/ext/picotcp/test/examples/mdns.c
@@ -0,0 +1,83 @@
+#include "utils.h"
+#include "pico_dns_common.h"
+#include "pico_mdns.h"
+#include "pico_ipv4.h"
+#include "pico_addressing.h"
+
+/*** START MDNS ***/
+
+#ifdef PICO_SUPPORT_MDNS
+
+#define SECONDS 10
+
+static int fully_initialized = 0;
+
+void mdns_init_callback( pico_mdns_rtree *rtree,
+ char *str,
+ void *arg )
+{
+ printf("\nInitialised with hostname: %s\n\n", str);
+
+ fully_initialized = 1;
+}
+
+void app_mdns(char *arg, struct pico_ip4 address)
+{
+ char *hostname, *peername;
+ char *nxt = arg;
+ uint64_t starttime = 0;
+ int once = 0;
+
+ if (!nxt)
+ exit(255);
+
+ nxt = cpy_arg(&hostname, nxt);
+ if(!hostname) {
+ exit(255);
+ }
+
+ if(!nxt) {
+ printf("Not enough args supplied!\n");
+ exit(255);
+ }
+
+ nxt = cpy_arg(&peername, nxt);
+ if(!peername) {
+ exit(255);
+ }
+
+ printf("\nStarting mDNS module...\n");
+ if (pico_mdns_init(hostname, address, &mdns_init_callback, NULL)) {
+ printf("Initialisation returned with Error!\n");
+ exit(255);
+ }
+
+ printf("\nTry reinitialising mDNS\n");
+ if (pico_mdns_init(hostname, address, &mdns_init_callback, NULL)) {
+ printf("Initialisation returned with Error!\n");
+ exit(255);
+ }
+
+ printf("DONE - Re-initialising mDNS module.\n");
+
+ starttime = PICO_TIME_MS();
+ printf("Starting time: %d\n", starttime);
+
+ while(1) {
+ pico_stack_tick();
+ usleep(2000);
+
+ if (((PICO_TIME_MS() - starttime) > SECONDS * 1000) && fully_initialized && !once) {
+ printf("\nTry reinitialising mDNS (a second time)\n");
+ if (pico_mdns_init(hostname, address, &mdns_init_callback, NULL)) {
+ printf("Initialisation returned with Error!\n");
+ exit(255);
+ }
+ once = 1;
+ printf("DONE - Re-initialising mDNS module. (a second time)\n");
+ }
+
+ }
+}
+#endif
+/*** END MDNS ***/
diff --git a/ext/picotcp/test/examples/multicast_ip6_recv.c b/ext/picotcp/test/examples/multicast_ip6_recv.c
new file mode 100644
index 0000000..22055fc
--- /dev/null
+++ b/ext/picotcp/test/examples/multicast_ip6_recv.c
@@ -0,0 +1,182 @@
+#include "utils.h"
+#include
+#include
+
+extern void app_udpecho(char *arg);
+
+/*** START Multicast RECEIVE + ECHO ***/
+/*
+ * multicast receive expects the following format: mcastreceive:link_addr:mcast_addr:listen_port:sendto_port
+ * link_addr: mcastreceive picoapp IP address
+ * mcast_addr: multicast IP address to receive
+ * listen_port: port number on which the mcastreceive listens
+ * sendto_port: port number to echo multicast traffic to (echo to originating IP address)
+ *
+ * f.e.: ./build/test/picoapp.elf --vde pic1:/tmp/pic0.ctl:10.40.0.3:255.255.0.0: -a mcastreceive:10.40.0.3:224.7.7.7:6667:6667
+ */
+extern struct udpclient_pas *udpclient_pas;
+extern struct udpecho_pas *udpecho_pas;
+#ifdef PICO_SUPPORT_MCAST
+void app_mcastreceive_ipv6(char *arg)
+{
+ char *new_arg = NULL, *p = NULL, *nxt = arg;
+ char *laddr = NULL, *maddr = NULL, *lport = NULL, *sport = NULL;
+ uint16_t listen_port = 0;
+ union pico_address inaddr_link = {
+ 0
+ }, inaddr_mcast = {
+ 0
+ }, src[5] = {
+ {.ip6 = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0xac, 0x10, 0x01, 0 }},
+ {.ip6 = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0xac, 0x10, 0x01, 0x10}},
+ {.ip6 = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0xac, 0x10, 0x01, 0x01 }},
+ {.ip6 = { 0xff, 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0xe0, 0x01, 0x01, 0x01 }},
+ };
+ struct pico_ip_mreq mreq = ZERO_MREQ_IP6;
+ struct pico_ip_mreq_source mreq_source = ZERO_MREQ_SRC_IP6;
+ /* start of parameter parsing */
+ if (nxt) {
+ nxt = cpy_arg(&laddr, nxt);
+ if (laddr) {
+ pico_string_to_ipv6(laddr, &inaddr_link.ip6.addr[0]);
+ } else {
+ goto out;
+ }
+ } else {
+ /* no arguments */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&maddr, nxt);
+ if (maddr) {
+ pico_string_to_ipv6(maddr, &inaddr_mcast.ip6.addr[0]);
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing multicast address */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&lport, nxt);
+ if (lport && atoi(lport)) {
+ listen_port = short_be(atoi(lport));
+ } else {
+ /* incorrect listen_port */
+ goto out;
+ }
+ } else {
+ /* missing listen_port */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&sport, nxt);
+ if (sport && atoi(sport)) {
+ /* unused at this moment */
+ /* send_port = short_be(atoi(sport)); */
+ } else {
+ /* incorrect send_port */
+ goto out;
+ }
+ } else {
+ /* missing send_port */
+ goto out;
+ }
+
+ /* end of parameter parsing */
+
+ printf("\n%s: multicast receive started. Receiving packets on [%s]:%d\n\n", __FUNCTION__, maddr, short_be(listen_port));
+
+ /* udpecho:bind_addr:listen_port[:sendto_port:datasize] */
+ new_arg = calloc(1, strlen(laddr) + 1 + strlen(lport) + 1 + strlen(sport) + strlen(",64:") + 1);
+ p = strcat(new_arg, laddr);
+ p = strcat(p + strlen(laddr), ",");
+ p = strcat(p + 1, lport);
+ p = strcat(p + strlen(lport), ",");
+ p = strcat(p + 1, sport);
+ p = strcat(p + strlen(sport), ",64,");
+
+ /* DAD needs to verify the link address before we can continue */
+ while(!pico_ipv6_link_get(&inaddr_link.ip6)) {
+ pico_stack_tick();
+ usleep(2000);
+ }
+ app_udpecho(new_arg);
+
+ memcpy(&mreq.mcast_group_addr, &inaddr_mcast, sizeof(struct pico_ip6));
+ memcpy( &mreq_source.mcast_group_addr, &inaddr_mcast, sizeof(struct pico_ip6));
+ memcpy(&mreq.mcast_link_addr, &inaddr_link, sizeof(struct pico_ip6));
+ memcpy(&mreq_source.mcast_link_addr, &inaddr_link, sizeof(struct pico_ip6));
+ memcpy(&mreq_source.mcast_source_addr, &src[0], sizeof(struct pico_ip6));
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_BLOCK_SOURCE, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_BLOCK_SOURCE failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_UNBLOCK_SOURCE, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_UNBLOCK_SOURCE failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ memcpy(&mreq_source.mcast_source_addr, &src[1], sizeof(struct pico_ip6));
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ memcpy(&mreq_source.mcast_source_addr, &src[2], sizeof(struct pico_ip6));
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ memcpy(&mreq_source.mcast_group_addr, &src[3], sizeof(struct pico_ip6));
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ return;
+
+out:
+ fprintf(stderr, "mcastreceive expects the following format: mcastreceive:link_addr:mcast_addr:listen_port[:send_port]\n");
+ exit(255);
+}
+#else
+void app_mcastreceive_ipv6(char *arg)
+{
+ printf("ERROR: PICO_SUPPORT_MCAST disabled\n");
+ return;
+}
+#endif
+/*** END Multicast RECEIVE + ECHO ***/
diff --git a/ext/picotcp/test/examples/multicast_ip6_send.c b/ext/picotcp/test/examples/multicast_ip6_send.c
new file mode 100644
index 0000000..1b2615b
--- /dev/null
+++ b/ext/picotcp/test/examples/multicast_ip6_send.c
@@ -0,0 +1,140 @@
+#include "utils.h"
+#include
+#include
+#include
+
+extern void app_udpclient(char *arg);
+/*** START Multicast SEND ***/
+/*
+ * multicast send expects the following format: mcastsend:link_addr:mcast_addr:sendto_port:listen_port
+ * link_addr: mcastsend picoapp IP address
+ * mcast_addr: multicast IP address to send to
+ * sendto_port: port number to send multicast traffic to
+ * listen_port: port number on which the mcastsend can receive data
+ *
+ * f.e.: ./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.255.0: -a mcastsend:10.40.0.2:224.7.7.7:6667:6667
+ */
+extern struct udpclient_pas *udpclient_pas;
+#ifdef PICO_SUPPORT_MCAST
+void app_mcastsend_ipv6(char *arg)
+{
+ int retval = 0;
+ char *maddr = NULL, *laddr = NULL, *lport = NULL, *sport = NULL;
+ uint16_t sendto_port = 0;
+ struct pico_ip6 inaddr_link = {
+ 0
+ }, inaddr_mcast = {
+ 0
+ };
+ char *new_arg = NULL, *p = NULL, *nxt = arg;
+ struct pico_ip_mreq mreq = ZERO_MREQ_IP6;
+
+ /* start of parameter parsing */
+ if (nxt) {
+ nxt = cpy_arg(&laddr, nxt);
+ if (laddr) {
+ pico_string_to_ipv6(laddr, &inaddr_link.addr[0]);
+ } else {
+ goto out;
+ }
+ } else {
+ /* no arguments */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&maddr, nxt);
+ if (maddr) {
+ pico_string_to_ipv6(maddr, &inaddr_mcast.addr[0]);
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing multicast address */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&sport, nxt);
+ if (sport && atoi(sport)) {
+ sendto_port = short_be(atoi(sport));
+ } else {
+ /* incorrect send_port */
+ goto out;
+ }
+ } else {
+ /* missing send_port */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&lport, nxt);
+ if (lport && atoi(lport)) {
+ /* unused at this moment */
+ /* listen_port = short_be(atoi(lport)); */
+ } else {
+ /* incorrect listen_port */
+ goto out;
+ }
+ } else {
+ /* missing listen_port */
+ goto out;
+ }
+
+ picoapp_dbg("\n%s: mcastsend started. Sending packets to %s:%u\n\n", __FUNCTION__, maddr, short_be(sendto_port));
+
+ /* udpclient:dest_addr:sendto_port[:listen_port:datasize:loops:subloops] */
+ new_arg = calloc(1, strlen(maddr) + 1 + strlen(sport) + 1 + strlen(lport) + strlen(",64,10,5,") + 1);
+ p = strcat(new_arg, maddr);
+ p = strcat(p + strlen(maddr), ",");
+ p = strcat(p + 1, sport);
+ p = strcat(p + strlen(sport), ",");
+ p = strcat(p + 1, lport);
+ p = strcat(p + strlen(lport), ",64,10,5,");
+
+ /* DAD needs to verify the link address before we can continue */
+ while(!pico_ipv6_link_get(&inaddr_link)) {
+ pico_stack_tick();
+ usleep(2000);
+ }
+ app_udpclient(new_arg);
+
+ memcpy(&mreq.mcast_group_addr, &inaddr_mcast, sizeof(struct pico_ip6));
+ memcpy(&mreq.mcast_link_addr, &inaddr_link, sizeof(struct pico_ip6));
+ if(pico_socket_setoption(udpclient_pas->s, PICO_IP_ADD_MEMBERSHIP, &mreq) < 0) {
+ picoapp_dbg("%s: socket_setoption PICO_IP_ADD_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ retval = 1;
+ }
+
+ if (new_arg)
+ free(new_arg);
+
+ if (lport)
+ free(lport);
+
+ if (maddr)
+ free(maddr);
+
+ if (sport)
+ free(sport);
+
+ if (laddr)
+ free(laddr);
+
+ if (retval)
+ exit(retval);
+
+ return;
+
+out:
+ picoapp_dbg("mcastsend expects the following format: mcastsend:link_addr:mcast_addr:sendto_port:listen_port\n");
+ exit(255);
+}
+#else
+void app_mcastsend_ipv6(char *arg)
+{
+ picoapp_dbg("ERROR: PICO_SUPPORT_MCAST disabled\n");
+ return;
+}
+#endif
+/*** END Multicast SEND ***/
diff --git a/ext/picotcp/test/examples/multicast_recv.c b/ext/picotcp/test/examples/multicast_recv.c
new file mode 100644
index 0000000..8e95e26
--- /dev/null
+++ b/ext/picotcp/test/examples/multicast_recv.c
@@ -0,0 +1,171 @@
+#include "utils.h"
+#include
+#include
+
+extern void app_udpecho(char *arg);
+
+/*** START Multicast RECEIVE + ECHO ***/
+/*
+ * multicast receive expects the following format: mcastreceive:link_addr:mcast_addr:listen_port:sendto_port
+ * link_addr: mcastreceive picoapp IP address
+ * mcast_addr: multicast IP address to receive
+ * listen_port: port number on which the mcastreceive listens
+ * sendto_port: port number to echo multicast traffic to (echo to originating IP address)
+ *
+ * f.e.: ./build/test/picoapp.elf --vde pic1:/tmp/pic0.ctl:10.40.0.3:255.255.0.0: -a mcastreceive:10.40.0.3:224.7.7.7:6667:6667
+ */
+extern struct udpclient_pas *udpclient_pas;
+extern struct udpecho_pas *udpecho_pas;
+#ifdef PICO_SUPPORT_MCAST
+void app_mcastreceive(char *arg)
+{
+ char *new_arg = NULL, *p = NULL, *nxt = arg;
+ char *laddr = NULL, *maddr = NULL, *lport = NULL, *sport = NULL;
+ uint16_t listen_port = 0;
+ union pico_address inaddr_link = {
+ 0
+ }, inaddr_mcast = {
+ 0
+ };
+ struct pico_ip_mreq mreq = ZERO_MREQ;
+ struct pico_ip_mreq_source mreq_source = ZERO_MREQ_SRC;
+
+ /* start of parameter parsing */
+ if (nxt) {
+ nxt = cpy_arg(&laddr, nxt);
+ if (laddr) {
+ pico_string_to_ipv4(laddr, &inaddr_link.ip4.addr);
+ } else {
+ goto out;
+ }
+ } else {
+ /* no arguments */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&maddr, nxt);
+ if (maddr) {
+ pico_string_to_ipv4(maddr, &inaddr_mcast.ip4.addr);
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing multicast address */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&lport, nxt);
+ if (lport && atoi(lport)) {
+ listen_port = short_be(atoi(lport));
+ } else {
+ /* incorrect listen_port */
+ goto out;
+ }
+ } else {
+ /* missing listen_port */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&sport, nxt);
+ if (sport && atoi(sport)) {
+ /* unused at this moment */
+ /* send_port = short_be(atoi(sport)); */
+ } else {
+ /* incorrect send_port */
+ goto out;
+ }
+ } else {
+ /* missing send_port */
+ goto out;
+ }
+
+ /* end of parameter parsing */
+
+ printf("\n%s: multicast receive started. Receiving packets on %s:%d\n\n", __FUNCTION__, maddr, short_be(listen_port));
+
+ /* udpecho:bind_addr:listen_port[:sendto_port:datasize] */
+ new_arg = calloc(1, strlen(laddr) + 1 + strlen(lport) + 1 + strlen(sport) + strlen(":64:") + 1);
+ p = strcat(new_arg, laddr);
+ p = strcat(p + strlen(laddr), ":");
+ p = strcat(p + 1, lport);
+ p = strcat(p + strlen(lport), ":");
+ p = strcat(p + 1, sport);
+ p = strcat(p + strlen(sport), ":64:");
+
+ app_udpecho(new_arg);
+
+ mreq.mcast_group_addr = mreq_source.mcast_group_addr = inaddr_mcast;
+ mreq.mcast_link_addr = mreq_source.mcast_link_addr = inaddr_link;
+ mreq_source.mcast_source_addr.ip4.addr = long_be(0XAC100101);
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_BLOCK_SOURCE, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_BLOCK_SOURCE failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_UNBLOCK_SOURCE, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_UNBLOCK_SOURCE failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ mreq_source.mcast_source_addr.ip4.addr = long_be(0XAC10010A);
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_DROP_MEMBERSHIP, &mreq) < 0) {
+ printf("%s: socket_setoption PICO_IP_DROP_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ mreq_source.mcast_source_addr.ip4.addr = long_be(0XAC100101);
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ mreq_source.mcast_group_addr.ip4.addr = long_be(0XE0010101);
+ if(pico_socket_setoption(udpecho_pas->s, PICO_IP_ADD_SOURCE_MEMBERSHIP, &mreq_source) < 0) {
+ printf("%s: socket_setoption PICO_IP_ADD_SOURCE_MEMBERSHIP: %s\n", __FUNCTION__, strerror(pico_err));
+ }
+
+ return;
+
+out:
+ fprintf(stderr, "mcastreceive expects the following format: mcastreceive:link_addr:mcast_addr:listen_port[:send_port]\n");
+ exit(255);
+}
+#else
+void app_mcastreceive(char *arg)
+{
+ printf("ERROR: PICO_SUPPORT_MCAST disabled\n");
+ return;
+}
+#endif
+/*** END Multicast RECEIVE + ECHO ***/
diff --git a/ext/picotcp/test/examples/multicast_send.c b/ext/picotcp/test/examples/multicast_send.c
new file mode 100644
index 0000000..a0cd435
--- /dev/null
+++ b/ext/picotcp/test/examples/multicast_send.c
@@ -0,0 +1,129 @@
+#include "utils.h"
+#include
+#include
+
+extern void app_udpclient(char *arg);
+/*** START Multicast SEND ***/
+/*
+ * multicast send expects the following format: mcastsend:link_addr:mcast_addr:sendto_port:listen_port
+ * link_addr: mcastsend picoapp IP address
+ * mcast_addr: multicast IP address to send to
+ * sendto_port: port number to send multicast traffic to
+ * listen_port: port number on which the mcastsend can receive data
+ *
+ * f.e.: ./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.255.0: -a mcastsend:10.40.0.2:224.7.7.7:6667:6667
+ */
+extern struct udpclient_pas *udpclient_pas;
+#ifdef PICO_SUPPORT_MCAST
+void app_mcastsend(char *arg)
+{
+ char *maddr = NULL, *laddr = NULL, *lport = NULL, *sport = NULL;
+ uint16_t sendto_port = 0;
+ union pico_address inaddr_link = {
+ 0
+ }, inaddr_mcast = {
+ 0
+ };
+ char *new_arg = NULL, *p = NULL, *nxt = arg;
+ struct pico_ip_mreq mreq = ZERO_MREQ;
+
+ /* start of parameter parsing */
+ if (nxt) {
+ nxt = cpy_arg(&laddr, nxt);
+ if (laddr) {
+ pico_string_to_ipv4(laddr, &inaddr_link.ip4.addr);
+ } else {
+ goto out;
+ }
+ } else {
+ /* no arguments */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&maddr, nxt);
+ if (maddr) {
+ pico_string_to_ipv4(maddr, &inaddr_mcast.ip4.addr);
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing multicast address */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&sport, nxt);
+ if (sport && atoi(sport)) {
+ sendto_port = short_be(atoi(sport));
+ } else {
+ /* incorrect send_port */
+ goto out;
+ }
+ } else {
+ /* missing send_port */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&lport, nxt);
+ if (lport && atoi(lport)) {
+ /* unused at this moment */
+ /* listen_port = short_be(atoi(lport)); */
+ } else {
+ /* incorrect listen_port */
+ goto out;
+ }
+ } else {
+ /* missing listen_port */
+ goto out;
+ }
+
+ picoapp_dbg("\n%s: mcastsend started. Sending packets to %08X:%u\n\n", __FUNCTION__, long_be(inaddr_mcast.addr), short_be(sendto_port));
+
+ /* udpclient:dest_addr:sendto_port[:listen_port:datasize:loops:subloops] */
+ new_arg = calloc(1, strlen(maddr) + 1 + strlen(sport) + 1 + strlen(lport) + strlen(":64:10:5:") + 1);
+ p = strcat(new_arg, maddr);
+ p = strcat(p + strlen(maddr), ":");
+ p = strcat(p + 1, sport);
+ p = strcat(p + strlen(sport), ":");
+ p = strcat(p + 1, lport);
+ p = strcat(p + strlen(lport), ":64:10:5:");
+
+ app_udpclient(new_arg);
+ free(new_arg);
+
+ mreq.mcast_group_addr = inaddr_mcast;
+ mreq.mcast_link_addr = inaddr_link;
+ if(pico_socket_setoption(udpclient_pas->s, PICO_IP_ADD_MEMBERSHIP, &mreq) < 0) {
+ picoapp_dbg("%s: socket_setoption PICO_IP_ADD_MEMBERSHIP failed: %s\n", __FUNCTION__, strerror(pico_err));
+ exit(1);
+ }
+
+ /* free strdups */
+ if (maddr)
+ free(maddr);
+
+ if (laddr)
+ free(laddr);
+
+ if (lport)
+ free(lport);
+
+ if (sport)
+ free(sport);
+
+ return;
+
+out:
+ picoapp_dbg("mcastsend expects the following format: mcastsend:link_addr:mcast_addr:sendto_port:listen_port\n");
+ exit(255);
+}
+#else
+void app_mcastsend(char *arg)
+{
+ picoapp_dbg("ERROR: PICO_SUPPORT_MCAST disabled\n");
+ return;
+}
+#endif
+/*** END Multicast SEND ***/
diff --git a/ext/picotcp/test/examples/natbox.c b/ext/picotcp/test/examples/natbox.c
new file mode 100644
index 0000000..1f54ecb
--- /dev/null
+++ b/ext/picotcp/test/examples/natbox.c
@@ -0,0 +1,31 @@
+#include "utils.h"
+#include
+#include
+
+/*** START NATBOX ***/
+void app_natbox(char *arg)
+{
+ char *dest = NULL;
+ struct pico_ip4 ipdst, pub_addr, priv_addr;
+ struct pico_ipv4_link *link;
+
+ cpy_arg(&dest, arg);
+ if (!dest) {
+ fprintf(stderr, "natbox needs the following format: natbox:dst_addr\n");
+ exit(255);
+ }
+
+ pico_string_to_ipv4(dest, &ipdst.addr);
+ link = pico_ipv4_link_get(&ipdst);
+ if (!link) {
+ fprintf(stderr, "natbox: Destination not found.\n");
+ exit(255);
+ }
+
+ pico_ipv4_nat_enable(link);
+ pico_string_to_ipv4("10.50.0.10", &pub_addr.addr);
+ pico_string_to_ipv4("10.40.0.08", &priv_addr.addr);
+ pico_ipv4_port_forward(pub_addr, short_be(5555), priv_addr, short_be(6667), PICO_PROTO_UDP, PICO_NAT_PORT_FORWARD_ADD);
+ fprintf(stderr, "natbox: started.\n");
+}
+/*** END NATBOX ***/
diff --git a/ext/picotcp/test/examples/noop.c b/ext/picotcp/test/examples/noop.c
new file mode 100644
index 0000000..d7dd5a4
--- /dev/null
+++ b/ext/picotcp/test/examples/noop.c
@@ -0,0 +1,11 @@
+/* NOOP */
+#include
+void app_noop(void)
+{
+ while(1) {
+ pico_stack_tick();
+ usleep(2000);
+ }
+}
+
+/* END NOOP */
diff --git a/ext/picotcp/test/examples/ping.c b/ext/picotcp/test/examples/ping.c
new file mode 100644
index 0000000..7d33709
--- /dev/null
+++ b/ext/picotcp/test/examples/ping.c
@@ -0,0 +1,138 @@
+#include "utils.h"
+#include
+#include
+#include
+#include
+/*** START PING ***/
+#ifdef PICO_SUPPORT_PING
+#define NUM_PING 10
+
+void cb_ping(struct pico_icmp4_stats *s)
+{
+ char host[30];
+ pico_ipv4_to_string(host, s->dst.addr);
+ if (s->err == 0) {
+ dbg("%lu bytes from %s: icmp_req=%lu ttl=%lu time=%lu ms\n", s->size, host, s->seq,
+ s->ttl, (long unsigned int)s->time);
+ if (s->seq >= NUM_PING)
+ exit(0);
+ } else {
+ dbg("PING %lu to %s: Error %d\n", s->seq, host, s->err);
+ exit(1);
+ }
+}
+
+#ifdef PICO_SUPPORT_IPV6
+void cb_ping6(struct pico_icmp6_stats *s)
+{
+ char host[50];
+ pico_ipv6_to_string(host, s->dst.addr);
+ if (s->err == 0) {
+ dbg("%lu bytes from %s: icmp_req=%lu ttl=%lu time=%lu ms\n", s->size, host, s->seq,
+ s->ttl, (long unsigned int)s->time);
+ if (s->seq >= NUM_PING)
+ exit(0);
+ } else {
+ dbg("PING %lu to %s: Error %d\n", s->seq, host, s->err);
+ exit(1);
+ }
+}
+#endif
+
+void ping_abort_timer(pico_time now, void *_id)
+{
+ int *id = (int *) _id;
+ printf("Ping: aborting...\n");
+ if (!IPV6_MODE)
+ pico_icmp4_ping_abort(*id);
+
+#ifdef PICO_SUPPORT_IPV6
+ else
+ pico_icmp6_ping_abort(*id);
+#endif
+}
+
+void app_ping(char *arg)
+{
+ char *dest = NULL;
+ char *next = NULL;
+ char *abort = NULL;
+ char *delay = NULL;
+ char *asize = NULL;
+ int initial_delay = 0;
+ struct pico_ip6 dst;
+ static int id;
+ int timeout = 0;
+ int size = 64;
+
+ next = cpy_arg(&dest, arg);
+ if (!dest) {
+ fprintf(stderr, "ping needs the following format: ping:dst_addr:[size:[abort after N sec:[wait N sec before start]]]\n");
+ exit(255);
+ }
+ pico_string_to_ipv6(dest, dst.addr);
+ if (next) {
+ next = cpy_arg(&asize, next);
+ size = atoi(asize);
+ free(asize);
+ if (size <= 0) {
+ size = 64; /* Default */
+ }
+ }
+
+ if (next) {
+ next = cpy_arg(&abort, next);
+ if (strlen(abort) > 0) {
+ printf("Got arg: '%s'\n", abort);
+ timeout = atoi(abort);
+ if (timeout < 0) {
+ fprintf(stderr, "ping needs the following format: ping:dst_addr:[size:[abort after N sec:[wait N sec before start]]]\n");
+ exit(255);
+ }
+ printf("Aborting ping after %d seconds\n", timeout);
+ }
+ }
+
+ if (next) {
+ next = cpy_arg(&delay, next);
+ if (strlen(delay) > 0) {
+ initial_delay = atoi(delay);
+ if (initial_delay > 0) {
+ printf("Initial delay: %d seconds\n", initial_delay);
+ initial_delay = PICO_TIME_MS() + initial_delay * 1000;
+ while (PICO_TIME_MS() < initial_delay) {
+ pico_stack_tick();
+ usleep(10000);
+ }
+ }
+ }
+ free(delay);
+ }
+ printf("Starting ping.\n");
+
+ if (!IPV6_MODE)
+ id = pico_icmp4_ping(dest, NUM_PING, 1000, 10000, size, cb_ping);
+
+#ifdef PICO_SUPPORT_IPV6
+ else
+ id = pico_icmp6_ping(dest, NUM_PING, 1000, 10000, size, cb_ping6, pico_ipv6_source_dev_find(&dst));
+#endif
+ if (timeout > 0) {
+ printf("Adding abort timer after %d seconds for id %d\n", timeout, id);
+ if (!pico_timer_add(timeout * 1000, ping_abort_timer, &id)) {
+ printf("Failed to set ping abort timeout, aborting ping\n");
+ ping_abort_timer((pico_time)0, &id);
+ exit(1);
+ }
+ }
+
+ /* free copied args */
+ if (dest)
+ free(dest);
+
+ if (abort)
+ free(abort);
+}
+#endif
+/*** END PING ***/
+
diff --git a/ext/picotcp/test/examples/slaacv4.c b/ext/picotcp/test/examples/slaacv4.c
new file mode 100644
index 0000000..a4ca62d
--- /dev/null
+++ b/ext/picotcp/test/examples/slaacv4.c
@@ -0,0 +1,72 @@
+#include "utils.h"
+#include
+#include
+/*** START SLAACV4 ***/
+
+void ping_callback_slaacv4(struct pico_icmp4_stats *s)
+{
+ char host[30] = { };
+
+ pico_ipv4_to_string(host, s->dst.addr);
+ if (s->err == 0) {
+ dbg("SLAACV4: %lu bytes from %s: icmp_req=%lu ttl=64 time=%lu ms\n", s->size, host,
+ s->seq, (long unsigned int)s->time);
+ if (s->seq >= 3) {
+ dbg("SLAACV4: TEST SUCCESS!\n");
+ pico_slaacv4_unregisterip();
+ exit(0);
+ }
+ } else {
+ dbg("SLAACV4: ping %lu to %s error %d\n", s->seq, host, s->err);
+ dbg("SLAACV4: TEST FAILED!\n");
+ exit(1);
+ }
+}
+
+void slaacv4_cb(struct pico_ip4 *ip, uint8_t code)
+{
+ char dst[16] = "169.254.22.5";
+ printf("SLAACV4 CALLBACK ip:0x%X code:%d \n", ip->addr, code);
+ if (code == 0)
+ {
+#ifdef PICO_SUPPORT_PING
+ pico_icmp4_ping(dst, 3, 1000, 5000, 32, ping_callback_slaacv4);
+#else
+ exit(0);
+#endif
+ }
+ else
+ {
+ exit(255);
+ }
+
+}
+
+
+void app_slaacv4(char *arg)
+{
+ char *sdev = NULL;
+ char *nxt = arg;
+ struct pico_device *dev = NULL;
+
+ if (!nxt)
+ exit(255);
+
+ while (nxt) {
+ if (nxt) {
+ nxt = cpy_arg(&sdev, nxt);
+ if(!sdev) {
+ exit(255);
+ }
+ }
+ }
+ dev = pico_get_device(sdev);
+ free(sdev);
+ if(dev == NULL) {
+ printf("%s: error getting device %s: %s\n", __FUNCTION__, dev->name, strerror(pico_err));
+ exit(255);
+ }
+
+ pico_slaacv4_claimip(dev, slaacv4_cb);
+}
+/*** END SLAACv4 ***/
diff --git a/ext/picotcp/test/examples/sntp.c b/ext/picotcp/test/examples/sntp.c
new file mode 100644
index 0000000..4a55dd3
--- /dev/null
+++ b/ext/picotcp/test/examples/sntp.c
@@ -0,0 +1,59 @@
+#include "utils.h"
+#include
+/*** START SNTP ***/
+
+#ifdef PICO_SUPPORT_SNTP_CLIENT
+
+void sntp_timeout(pico_time __attribute__((unused)) now, void *arg)
+{
+ struct pico_timeval ptv;
+ struct timeval tv;
+ pico_sntp_gettimeofday(&ptv);
+ gettimeofday(&tv, NULL);
+ printf("Linux sec: %u, msec: %u\n", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec / 1000);
+ printf("Picotcp sec: %u, msec: %u\n", (unsigned int)ptv.tv_sec, (unsigned int)ptv.tv_msec);
+ printf("SNTP test succesfull!\n");
+ exit(0);
+}
+
+void cb_synced(pico_err_t status)
+{
+ if(status == PICO_ERR_ENETDOWN) {
+ printf("SNTP: Cannot resolve ntp server name\n");
+ exit(1);
+ } else if (status == PICO_ERR_ETIMEDOUT) {
+ printf("SNTP: Timed out, did not receive ntp packet from server\n");
+ exit(1);
+ } else if (status == PICO_ERR_EINVAL) {
+ printf("SNTP: Conversion error\n");
+ exit(1);
+ } else if (status == PICO_ERR_ENOTCONN) {
+ printf("SNTP: Socket error\n");
+ exit(1);
+ } else if (status == PICO_ERR_NOERR) {
+ if (!pico_timer_add(2000, sntp_timeout, NULL)) {
+ printf("SNTP: Failed to start timeout timer, exiting program \n");
+ exit(1);
+ }
+ } else {
+ printf("SNTP: Invalid status received in cb_synced\n");
+ exit(1);
+ }
+}
+
+void app_sntp(char *servername)
+{
+ struct pico_timeval tv;
+ printf("Starting SNTP query towards %s\n", servername);
+ if(pico_sntp_gettimeofday(&tv) == 0)
+ printf("Wrongly succesfull gettimeofday\n");
+ else
+ printf("Unsuccesfull gettimeofday (not synced)\n");
+
+ if(pico_sntp_sync(servername, &cb_synced) == 0)
+ printf("Succesfull sync call!\n");
+ else
+ printf("Error in sync\n");
+}
+#endif
+/*** END SNTP ***/
diff --git a/ext/picotcp/test/examples/tcpbench.c b/ext/picotcp/test/examples/tcpbench.c
new file mode 100644
index 0000000..39b29a4
--- /dev/null
+++ b/ext/picotcp/test/examples/tcpbench.c
@@ -0,0 +1,320 @@
+#include "utils.h"
+#include
+#include
+#include
+/*** START TCP BENCH ***/
+#define TCP_BENCH_TX 1
+#define TCP_BENCH_RX 2
+#define TCP_BENCH_TX_FOREVER 3
+static char *buffer1;
+static char *buffer0;
+
+int tcpbench_mode = 0;
+struct pico_socket *tcpbench_sock = NULL;
+static pico_time tcpbench_time_start, tcpbench_time_end;
+
+void cb_tcpbench(uint16_t ev, struct pico_socket *s)
+{
+ static int closed = 0;
+ static unsigned long count = 0;
+ uint8_t recvbuf[1500];
+ uint16_t port;
+ char peer[200];
+ /* struct pico_socket *sock_a; */
+
+ static int tcpbench_wr_size = 0;
+ static int tcpbench_rd_size = 0;
+ int tcpbench_w = 0;
+ int tcpbench_r = 0;
+ double tcpbench_time = 0;
+
+ count++;
+
+ if (ev & PICO_SOCK_EV_RD) {
+ do {
+ /* read data, but discard */
+ tcpbench_r = pico_socket_read(s, recvbuf, 1500);
+ if (tcpbench_r > 0) {
+ tcpbench_rd_size += tcpbench_r;
+ }
+ } while (tcpbench_r > 0);
+ if (tcpbench_time_start == 0)
+ tcpbench_time_start = PICO_TIME_MS();
+
+ printf("tcpbench_rd_size = %d \r", tcpbench_rd_size);
+ }
+
+ if (ev & PICO_SOCK_EV_CONN) {
+ if (!IPV6_MODE) {
+ struct pico_ip4 orig;
+ if (tcpbench_mode == TCP_BENCH_TX || tcpbench_mode == TCP_BENCH_TX_FOREVER) {
+ printf("tcpbench> Connection established with server.\n");
+ } else if (tcpbench_mode == TCP_BENCH_RX) {
+ /* sock_a = pico_socket_accept(s, &orig, &port); */
+ pico_socket_accept(s, &orig, &port);
+ pico_ipv4_to_string(peer, orig.addr);
+ printf("tcpbench> Connection established with %s:%d.\n", peer, short_be(port));
+ }
+ } else {
+ struct pico_ip6 orig;
+ if (tcpbench_mode == TCP_BENCH_TX || tcpbench_mode == TCP_BENCH_TX_FOREVER) {
+ printf("tcpbench> Connection established with server.\n");
+ } else if (tcpbench_mode == TCP_BENCH_RX) {
+ /* sock_a = pico_socket_accept(s, &orig, &port); */
+ pico_socket_accept(s, &orig, &port);
+#ifdef PICO_SUPPORT_IPV6
+ pico_ipv6_to_string(peer, orig.addr);
+ printf("tcpbench> Connection established with [%s]:%d.\n", peer, short_be(port));
+#endif
+ }
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_FIN) {
+ printf("tcpbench> Socket closed. Exit normally. \n");
+ if (tcpbench_mode == TCP_BENCH_RX) {
+ tcpbench_time_end = PICO_TIME_MS();
+ tcpbench_time = (tcpbench_time_end - tcpbench_time_start) / 1000.0; /* get number of seconds */
+ printf("tcpbench> received %d bytes in %lf seconds\n", tcpbench_rd_size, tcpbench_time);
+ printf("tcpbench> average read throughput %lf kbit/sec\n", ((tcpbench_rd_size * 8.0) / tcpbench_time) / 1000);
+ pico_socket_shutdown(s, PICO_SHUT_WR);
+ printf("tcpbench> Called shutdown write, ev = %d\n", ev);
+ }
+
+ if (!pico_timer_add(5000, deferred_exit, NULL)) {
+ printf("tcpbench> Failed to start exit timer, exiting now\n");
+ exit(1);
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_ERR) {
+ printf("tcpbench> ---- Socket Error received: %s. Bailing out.\n", strerror(pico_err));
+ if (!pico_err == PICO_ERR_ECONNRESET) {
+ if (pico_timer_add(5000, deferred_exit, NULL)) {
+ printf("tcpbench> Failed to start exit timer, exiting now\n");
+ exit(1);
+ }
+ }
+ else {
+ printf("tcpbench> ---- Socket Error: '%s'. Was unexpected! Something went wrong.\n", strerror(pico_err));
+ exit(2);
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_CLOSE) {
+ printf("tcpbench> event close\n");
+ if (tcpbench_mode == TCP_BENCH_RX) {
+ pico_socket_close(s);
+ printf("tcpbench> Called shutdown write, ev = %d\n", ev);
+ } else if (tcpbench_mode == TCP_BENCH_TX || tcpbench_mode == TCP_BENCH_TX_FOREVER) {
+ pico_socket_close(s);
+ return;
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_WR) {
+ if (((tcpbench_wr_size < TCPSIZ) && (tcpbench_mode == TCP_BENCH_TX)) || tcpbench_mode == TCP_BENCH_TX_FOREVER) {
+ do {
+ tcpbench_w = pico_socket_write(tcpbench_sock, buffer0 + (tcpbench_wr_size % TCPSIZ), TCPSIZ - (tcpbench_wr_size % TCPSIZ));
+ if (tcpbench_w > 0) {
+ tcpbench_wr_size += tcpbench_w;
+ /* printf("tcpbench> SOCKET WRITTEN - %d\n",tcpbench_w); */
+ } else {
+ /* printf("pico_socket_write returned %d\n", tcpbench_w); */
+ }
+
+ if (tcpbench_time_start == 0)
+ tcpbench_time_start = PICO_TIME_MS();
+ } while(tcpbench_w > 0);
+ printf("tcpbench_wr_size = %d \r", tcpbench_wr_size);
+ } else {
+ if (!closed && tcpbench_mode == TCP_BENCH_TX) {
+ tcpbench_time_end = PICO_TIME_MS();
+ pico_socket_shutdown(s, PICO_SHUT_WR);
+ printf("tcpbench> TCPSIZ written\n");
+ printf("tcpbench> Called shutdown()\n");
+ tcpbench_time = (tcpbench_time_end - tcpbench_time_start) / 1000.0; /* get number of seconds */
+ printf("tcpbench> Transmitted %u bytes in %lf seconds\n", TCPSIZ, tcpbench_time);
+ printf("tcpbench> average write throughput %lf kbit/sec\n", ((TCPSIZ * 8.0) / tcpbench_time) / 1000);
+ closed = 1;
+ }
+ }
+ }
+}
+
+void app_tcpbench(char *arg)
+{
+ struct pico_socket *s;
+ char *dport = NULL;
+ char *dest = NULL;
+ char *mode = NULL;
+ char *nagle = NULL;
+ int port = 0, i;
+ uint16_t port_be = 0;
+ char *nxt;
+ char *sport = NULL;
+ int nagle_off = 1;
+ union {
+ struct pico_ip4 ip4;
+ struct pico_ip6 ip6;
+ } inaddr_any = {
+ .ip4 = {0}, .ip6 = {{0}}
+ };
+
+ nxt = cpy_arg(&mode, arg);
+
+ if ((*mode == 't') || (*mode == 'f')) { /* TEST BENCH SEND MODE */
+ if (*mode == 't')
+ tcpbench_mode = TCP_BENCH_TX;
+ else
+ tcpbench_mode = TCP_BENCH_TX_FOREVER;
+
+ printf("tcpbench> TX\n");
+
+ nxt = cpy_arg(&dest, nxt);
+ if (!dest) {
+ fprintf(stderr, "tcpbench send needs the following format: tcpbench:tx:dst_addr[:dport][:n] -- 'n' is for nagle\n");
+ exit(255);
+ }
+
+ printf ("+++ Dest is %s\n", dest);
+ if (nxt) {
+ printf("Next arg: %s\n", nxt);
+ nxt = cpy_arg(&dport, nxt);
+ printf("Dport: %s\n", dport);
+ }
+
+ if (nxt) {
+ printf("Next arg: %s\n", nxt);
+ nxt = cpy_arg(&nagle, nxt);
+ printf("nagle: %s\n", nagle);
+ if (strlen(nagle) == 1 && nagle[0] == 'n') {
+ nagle_off = 0;
+ printf("Nagle algorithm enabled\n");
+ }
+ }
+
+ if (dport) {
+ port = atoi(dport);
+ port_be = short_be((uint16_t)port);
+ }
+
+ if (port == 0) {
+ port_be = short_be(5555);
+ }
+
+ buffer0 = malloc(TCPSIZ);
+ buffer1 = malloc(TCPSIZ);
+ printf("Buffer1 (%p)\n", buffer1);
+ for (i = 0; i < TCPSIZ; i++) {
+ char c = (i % 26) + 'a';
+ buffer0[i] = c;
+ }
+ memset(buffer1, 'a', TCPSIZ);
+ printf("tcpbench> Connecting to: %s:%d\n", dest, short_be(port_be));
+
+ if (!IPV6_MODE) {
+ struct pico_ip4 server_addr;
+ s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &cb_tcpbench);
+ if (!s)
+ exit(1);
+
+ pico_socket_setoption(s, PICO_TCP_NODELAY, &nagle_off);
+
+ /* NOTE: used to set a fixed local port and address
+ local_port = short_be(6666);
+ pico_string_to_ipv4("10.40.0.11", &local_addr.addr);
+ pico_socket_bind(s, &local_addr, &local_port);*/
+
+ pico_string_to_ipv4(dest, &server_addr.addr);
+ pico_socket_connect(s, &server_addr, port_be);
+ } else {
+ struct pico_ip6 server_addr;
+ s = pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_TCP, &cb_tcpbench);
+ if (!s)
+ exit(1);
+
+ pico_socket_setoption(s, PICO_TCP_NODELAY, &nagle_off);
+
+ /* NOTE: used to set a fixed local port and address
+ local_port = short_be(6666);
+ pico_string_to_ipv4("10.40.0.11", &local_addr.addr);
+ pico_socket_bind(s, &local_addr, &local_port);*/
+#ifdef PICO_SUPPORT_IPV6
+ pico_string_to_ipv6(dest, server_addr.addr);
+ pico_socket_connect(s, &server_addr, port_be);
+#endif
+
+ }
+
+ } else if (*mode == 'r') { /* TEST BENCH RECEIVE MODE */
+ int ret;
+ tcpbench_mode = TCP_BENCH_RX;
+ printf("tcpbench> RX\n");
+
+ cpy_arg(&sport, nxt);
+ if (!sport) {
+ fprintf(stderr, "tcpbench receive needs the following format: tcpbench:rx[:dport]\n");
+ exit(255);
+ }
+
+ if (sport) {
+ printf("s-port is %s\n", sport);
+ port = atoi(sport);
+ port_be = short_be((uint16_t)port);
+ printf("tcpbench> Got port %d\n", port);
+ free(sport);
+ }
+
+ if (port == 0) {
+ port_be = short_be(5555);
+ }
+
+ printf("tcpbench> OPEN\n");
+ if (!IPV6_MODE)
+ s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &cb_tcpbench);
+ else
+ s = pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_TCP, &cb_tcpbench);
+
+ if (!s)
+ exit(1);
+
+ printf("tcpbench> BIND\n");
+ if (!IPV6_MODE)
+ ret = pico_socket_bind(s, &inaddr_any.ip4, &port_be);
+ else
+ ret = pico_socket_bind(s, &inaddr_any.ip6, &port_be);
+
+ if (ret < 0) {
+ printf("tcpbench> BIND failed because %s\n", strerror(pico_err));
+ exit(1);
+ }
+
+ printf("tcpbench> LISTEN\n");
+ if (pico_socket_listen(s, 40) != 0)
+ exit(1);
+
+ printf("tcpbench> listening port %u ...\n", short_be(port_be));
+ } else {
+ printf("tcpbench> wrong mode argument\n");
+ exit(1);
+ }
+
+ tcpbench_sock = s;
+
+ /* free strdups */
+ if (dport)
+ free(dport);
+
+ if (dest)
+ free (dest);
+
+ if (mode)
+ free (mode);
+
+ if (nagle)
+ free (nagle);
+
+ return;
+}
+/*** END TCP BENCH ***/
diff --git a/ext/picotcp/test/examples/tcpclient.c b/ext/picotcp/test/examples/tcpclient.c
new file mode 100644
index 0000000..e81bddd
--- /dev/null
+++ b/ext/picotcp/test/examples/tcpclient.c
@@ -0,0 +1,197 @@
+#include "utils.h"
+#include
+#include
+#include
+/*** START TCP CLIENT ***/
+static char *buffer1;
+static char *buffer0;
+
+void compare_results(pico_time __attribute__((unused)) now, void __attribute__((unused)) *arg)
+{
+#ifdef CONSISTENCY_CHECK /* TODO: Enable */
+ int i;
+ printf("Calculating result.... (%p)\n", buffer1);
+
+ if (memcmp(buffer0, buffer1, TCPSIZ) == 0)
+ exit(0);
+
+ for (i = 0; i < TCPSIZ; i++) {
+ if (buffer0[i] != buffer1[i]) {
+ fprintf(stderr, "Error at byte %d - %c!=%c\n", i, buffer0[i], buffer1[i]);
+ exit(115);
+ }
+ }
+#endif
+ exit(0);
+
+}
+
+void cb_tcpclient(uint16_t ev, struct pico_socket *s)
+{
+ static int w_size = 0;
+ static int r_size = 0;
+ static int closed = 0;
+ int r, w;
+ static unsigned long count = 0;
+
+ count++;
+ picoapp_dbg("tcpclient> wakeup %lu, event %u\n", count, ev);
+
+ if (ev & PICO_SOCK_EV_RD) {
+ do {
+ r = pico_socket_read(s, buffer1 + r_size, TCPSIZ - r_size);
+ if (r > 0) {
+ r_size += r;
+ picoapp_dbg("SOCKET READ - %d\n", r_size);
+ }
+
+ if (r < 0)
+ exit(5);
+ } while(r > 0);
+ }
+
+ if (ev & PICO_SOCK_EV_CONN) {
+ printf("Connection established with server.\n");
+ }
+
+ if (ev & PICO_SOCK_EV_FIN) {
+ printf("Socket closed. Exit normally. \n");
+ if (!pico_timer_add(2000, compare_results, NULL)) {
+ printf("Failed to start exit timer, exiting now\n");
+ exit(1);
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_ERR) {
+ printf("Socket error received: %s. Bailing out.\n", strerror(pico_err));
+ exit(1);
+ }
+
+ if (ev & PICO_SOCK_EV_CLOSE) {
+ printf("Socket received close from peer - Wrong case if not all client data sent!\n");
+ pico_socket_close(s);
+ return;
+ }
+
+ if (ev & PICO_SOCK_EV_WR) {
+ if (w_size < TCPSIZ) {
+ do {
+ w = pico_socket_write(s, buffer0 + w_size, TCPSIZ - w_size);
+ if (w > 0) {
+ w_size += w;
+ picoapp_dbg("SOCKET WRITTEN - %d\n", w_size);
+ if (w < 0)
+ exit(5);
+ }
+ } while(w > 0);
+ } else {
+#ifdef INFINITE_TCPTEST
+ w_size = 0;
+ return;
+#endif
+ if (!closed) {
+ pico_socket_shutdown(s, PICO_SHUT_WR);
+ printf("Called shutdown()\n");
+ closed = 1;
+ }
+ }
+ }
+}
+
+void app_tcpclient(char *arg)
+{
+ char *daddr = NULL, *dport = NULL;
+ char *nxt = arg;
+ uint16_t send_port = 0, listen_port = short_be(5555);
+ int i = 0, ret = 0, yes = 1;
+ struct pico_socket *s = NULL;
+ union pico_address dst = {
+ .ip4 = {0}, .ip6 = {{0}}
+ };
+ union pico_address inaddr_any = {
+ .ip4 = {0}, .ip6 = {{0}}
+ };
+
+ /* start of argument parsing */
+ if (nxt) {
+ nxt = cpy_arg(&daddr, arg);
+ if (daddr) {
+ if (!IPV6_MODE)
+ pico_string_to_ipv4(daddr, &dst.ip4.addr);
+
+ #ifdef PICO_SUPPORT_IPV6
+ else
+ pico_string_to_ipv6(daddr, dst.ip6.addr);
+ #endif
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing dest_addr */
+ goto out;
+ }
+
+ if (nxt) {
+ nxt = cpy_arg(&dport, nxt);
+ if (dport && atoi(dport)) {
+ send_port = short_be(atoi(dport));
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing send_port */
+ goto out;
+ }
+
+ /* end of argument parsing */
+
+ buffer0 = malloc(TCPSIZ);
+ buffer1 = malloc(TCPSIZ);
+ printf("Buffer1 (%p)\n", buffer1);
+ for (i = 0; i < TCPSIZ; i++) {
+ char c = (i % 26) + 'a';
+ buffer0[i] = c;
+ }
+ memset(buffer1, 'a', TCPSIZ);
+
+ printf("Connecting to: %s:%d\n", daddr, short_be(send_port));
+
+ if (!IPV6_MODE)
+ s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &cb_tcpclient);
+ else
+ s = pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_TCP, &cb_tcpclient);
+
+ if (!s) {
+ printf("%s: error opening socket: %s\n", __FUNCTION__, strerror(pico_err));
+ exit(1);
+ }
+
+ pico_socket_setoption(s, PICO_TCP_NODELAY, &yes);
+
+ if (!IPV6_MODE)
+ ret = pico_socket_bind(s, &inaddr_any.ip4, &listen_port);
+ else
+ ret = pico_socket_bind(s, &inaddr_any.ip6, &listen_port);
+
+ if (ret < 0) {
+ printf("%s: error binding socket to port %u: %s\n", __FUNCTION__, short_be(listen_port), strerror(pico_err));
+ exit(1);
+ }
+
+ if (!IPV6_MODE)
+ ret = pico_socket_connect(s, &dst.ip4, send_port);
+ else
+ ret = pico_socket_connect(s, &dst.ip6, send_port);
+
+ if (ret < 0) {
+ printf("%s: error connecting to %s:%u: %s\n", __FUNCTION__, daddr, short_be(send_port), strerror(pico_err));
+ exit(1);
+ }
+
+ return;
+
+out:
+ fprintf(stderr, "tcpclient expects the following format: tcpclient:dest_addr:dest_port\n");
+ exit(255);
+}
+/*** END TCP CLIENT ***/
diff --git a/ext/picotcp/test/examples/tcpecho.c b/ext/picotcp/test/examples/tcpecho.c
new file mode 100644
index 0000000..b109d9c
--- /dev/null
+++ b/ext/picotcp/test/examples/tcpecho.c
@@ -0,0 +1,178 @@
+#include "utils.h"
+#include
+#include
+/*** START TCP ECHO ***/
+#define BSIZE (1024 * 10)
+static char recvbuf[BSIZE];
+static int pos = 0, len = 0;
+static int flag = 0;
+
+int send_tcpecho(struct pico_socket *s)
+{
+ int w, ww = 0;
+ if (len > pos) {
+ do {
+ w = pico_socket_write(s, recvbuf + pos, len - pos);
+ if (w > 0) {
+ pos += w;
+ ww += w;
+ if (pos >= len) {
+ pos = 0;
+ len = 0;
+ }
+ }
+ } while((w > 0) && (pos < len));
+ }
+
+ return ww;
+}
+
+void cb_tcpecho(uint16_t ev, struct pico_socket *s)
+{
+ int r = 0;
+
+ picoapp_dbg("tcpecho> wakeup ev=%u\n", ev);
+
+ if (ev & PICO_SOCK_EV_RD) {
+ if (flag & PICO_SOCK_EV_CLOSE)
+ printf("SOCKET> EV_RD, FIN RECEIVED\n");
+
+ while (len < BSIZE) {
+ r = pico_socket_read(s, recvbuf + len, BSIZE - len);
+ if (r > 0) {
+ len += r;
+ flag &= ~(PICO_SOCK_EV_RD);
+ } else {
+ flag |= PICO_SOCK_EV_RD;
+ break;
+ }
+ }
+ if (flag & PICO_SOCK_EV_WR) {
+ flag &= ~PICO_SOCK_EV_WR;
+ send_tcpecho(s);
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_CONN) {
+ uint32_t ka_val = 0;
+ struct pico_socket *sock_a = {
+ 0
+ };
+ struct pico_ip4 orig = {
+ 0
+ };
+ uint16_t port = 0;
+ char peer[30] = {
+ 0
+ };
+ int yes = 1;
+
+ sock_a = pico_socket_accept(s, &orig, &port);
+ pico_ipv4_to_string(peer, orig.addr);
+ printf("Connection established with %s:%d.\n", peer, short_be(port));
+ pico_socket_setoption(sock_a, PICO_TCP_NODELAY, &yes);
+ /* Set keepalive options */
+ ka_val = 5;
+ pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPCNT, &ka_val);
+ ka_val = 30000;
+ pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPIDLE, &ka_val);
+ ka_val = 5000;
+ pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPINTVL, &ka_val);
+ }
+
+ if (ev & PICO_SOCK_EV_FIN) {
+ printf("Socket closed. Exit normally. \n");
+ if (!pico_timer_add(2000, deferred_exit, NULL)) {
+ printf("Failed to start exit timer, exiting now\n");
+ exit(1);
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_ERR) {
+ printf("Socket error received: %s. Bailing out.\n", strerror(pico_err));
+ exit(1);
+ }
+
+ if (ev & PICO_SOCK_EV_CLOSE) {
+ printf("Socket received close from peer.\n");
+ if (flag & PICO_SOCK_EV_RD) {
+ pico_socket_shutdown(s, PICO_SHUT_WR);
+ printf("SOCKET> Called shutdown write, ev = %d\n", ev);
+ }
+ }
+
+ if (ev & PICO_SOCK_EV_WR) {
+ r = send_tcpecho(s);
+ if (r == 0)
+ flag |= PICO_SOCK_EV_WR;
+ else
+ flag &= (~PICO_SOCK_EV_WR);
+ }
+}
+
+void app_tcpecho(char *arg)
+{
+ char *nxt = arg;
+ char *lport = NULL;
+ uint16_t listen_port = 0;
+ int ret = 0, yes = 1;
+ struct pico_socket *s = NULL;
+ union {
+ struct pico_ip4 ip4;
+ struct pico_ip6 ip6;
+ } inaddr_any = {
+ .ip4 = {0}, .ip6 = {{0}}
+ };
+
+ /* start of argument parsing */
+ if (nxt) {
+ nxt = cpy_arg(&lport, nxt);
+ if (lport && atoi(lport)) {
+ listen_port = short_be(atoi(lport));
+ } else {
+ goto out;
+ }
+ } else {
+ /* missing listen_port */
+ goto out;
+ }
+
+ /* end of argument parsing */
+
+ if (!IPV6_MODE)
+ s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &cb_tcpecho);
+ else
+ s = pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_TCP, &cb_tcpecho);
+
+ if (!s) {
+ printf("%s: error opening socket: %s\n", __FUNCTION__, strerror(pico_err));
+ exit(1);
+ }
+
+ pico_socket_setoption(s, PICO_TCP_NODELAY, &yes);
+
+
+
+ if (!IPV6_MODE)
+ ret = pico_socket_bind(s, &inaddr_any.ip4, &listen_port);
+ else
+ ret = pico_socket_bind(s, &inaddr_any.ip6, &listen_port);
+
+ if (ret < 0) {
+ printf("%s: error binding socket to port %u: %s\n", __FUNCTION__, short_be(listen_port), strerror(pico_err));
+ exit(1);
+ }
+
+ if (pico_socket_listen(s, 40) != 0) {
+ printf("%s: error listening on port %u\n", __FUNCTION__, short_be(listen_port));
+ exit(1);
+ }
+
+ printf("Launching PicoTCP echo server\n");
+ return;
+
+out:
+ fprintf(stderr, "tcpecho expects the following format: tcpecho:listen_port\n");
+ exit(255);
+}
+/*** END TCP ECHO ***/
diff --git a/ext/picotcp/test/examples/tftp.c b/ext/picotcp/test/examples/tftp.c
new file mode 100644
index 0000000..d691ca4
--- /dev/null
+++ b/ext/picotcp/test/examples/tftp.c
@@ -0,0 +1,485 @@
+#include "utils.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include