Merge branch 'contiki'

Conflicts:
	cpu/cc26xx-cc13xx/lib/cc13xxware
	cpu/cc26xx-cc13xx/lib/cc26xxware
This commit is contained in:
Harald Pichler 2017-01-31 15:00:59 +01:00
commit 2f8549aaae
319 changed files with 58114 additions and 6745 deletions

View file

@ -29,6 +29,9 @@
*
*/
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
#include "contiki.h"
#include "sys/cc.h"
#include "contiki-net.h"
@ -37,10 +40,8 @@
#include "tcp-socket.h"
#include <stdio.h>
#include <string.h>
static void relisten(struct tcp_socket *s);
LIST(socketlist);
@ -80,7 +81,7 @@ acked(struct tcp_socket *s)
s->output_data_maxlen - s->output_data_send_nxt);
}
if(s->output_data_len < s->output_data_send_nxt) {
printf("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
PRINTF("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
s->output_data_len,
s->output_data_send_nxt);
tcp_markconn(uip_conn, NULL);
@ -121,7 +122,7 @@ newdata(struct tcp_socket *s)
bytesleft = 0;
}
if(bytesleft > 0) {
printf("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft);
PRINTF("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft);
}
dataptr += copylen;
len -= copylen;
@ -356,6 +357,8 @@ tcp_socket_send(struct tcp_socket *s,
s->output_senddata_len = s->output_data_len;
}
tcpip_poll_tcp(s->c);
return len;
}
/*---------------------------------------------------------------------------*/
@ -398,3 +401,9 @@ tcp_socket_max_sendlen(struct tcp_socket *s)
return s->output_data_maxlen - s->output_data_len;
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_queuelen(struct tcp_socket *s)
{
return s->output_data_len;
}
/*---------------------------------------------------------------------------*/

View file

@ -284,4 +284,16 @@ int tcp_socket_unregister(struct tcp_socket *s);
*/
int tcp_socket_max_sendlen(struct tcp_socket *s);
/**
* \brief The number of bytes waiting to be sent
* \param s A pointer to a TCP socket
* \return The number of bytes that have not yet been acknowledged by the receiver.
*
* This function queries the TCP socket and returns the
* number of bytes that are currently not yet known to
* have been successfully received by the receiver.
*
*/
int tcp_socket_queuelen(struct tcp_socket *s);
#endif /* TCP_SOCKET_H */

View file

@ -662,7 +662,7 @@ tcpip_ipv6_output(void)
nbr = uip_ds6_nbr_lookup(nexthop);
if(nbr == NULL) {
#if UIP_ND6_SEND_NA
#if UIP_ND6_SEND_NS
if((nbr = uip_ds6_nbr_add(nexthop, NULL, 0, NBR_INCOMPLETE, NBR_TABLE_REASON_IPV6_ND, NULL)) == NULL) {
uip_clear_buf();
PRINTF("tcpip_ipv6_output: failed to add neighbor to cache\n");
@ -691,13 +691,13 @@ tcpip_ipv6_output(void)
nbr->nscount = 1;
/* Send the first NS try from here (multicast destination IP address). */
}
#else /* UIP_ND6_SEND_NA */
#else /* UIP_ND6_SEND_NS */
PRINTF("tcpip_ipv6_output: neighbor not in cache\n");
uip_len = 0;
return;
#endif /* UIP_ND6_SEND_NA */
#endif /* UIP_ND6_SEND_NS */
} else {
#if UIP_ND6_SEND_NA
#if UIP_ND6_SEND_NS
if(nbr->state == NBR_INCOMPLETE) {
PRINTF("tcpip_ipv6_output: nbr cache entry incomplete\n");
#if UIP_CONF_IPV6_QUEUE_PKT
@ -719,7 +719,7 @@ tcpip_ipv6_output(void)
nbr->nscount = 0;
PRINTF("tcpip_ipv6_output: nbr cache entry stale moving to delay\n");
}
#endif /* UIP_ND6_SEND_NA */
#endif /* UIP_ND6_SEND_NS */
tcpip_output(uip_ds6_nbr_get_ll(nbr));

View file

@ -88,19 +88,23 @@ uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
, reason, data);
if(nbr) {
uip_ipaddr_copy(&nbr->ipaddr, ipaddr);
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
#if UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
nbr->isrouter = isrouter;
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
#endif /* UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
nbr->state = state;
#if UIP_CONF_IPV6_QUEUE_PKT
uip_packetqueue_new(&nbr->packethandle);
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
#if UIP_ND6_SEND_NA
/* timers are set separately, for now we put them in expired state */
stimer_set(&nbr->reachable, 0);
#if UIP_ND6_SEND_NS
if(nbr->state == NBR_REACHABLE) {
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
} else {
/* We set the timer in expired state */
stimer_set(&nbr->reachable, 0);
}
stimer_set(&nbr->sendns, 0);
nbr->nscount = 0;
#endif /* UIP_ND6_SEND_NA */
#endif /* UIP_ND6_SEND_NS */
PRINTF("Adding neighbor with ip addr ");
PRINT6ADDR(ipaddr);
PRINTF(" link addr ");
@ -241,7 +245,7 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
#endif /* UIP_DS6_LL_NUD */
}
#if UIP_ND6_SEND_NA
#if UIP_ND6_SEND_NS
/*---------------------------------------------------------------------------*/
/** Periodic processing on neighbors */
void
@ -322,6 +326,18 @@ uip_ds6_neighbor_periodic(void)
}
}
/*---------------------------------------------------------------------------*/
void
uip_ds6_nbr_refresh_reachable_state(const uip_ipaddr_t *ipaddr)
{
uip_ds6_nbr_t *nbr;
nbr = uip_ds6_nbr_lookup(ipaddr);
if(nbr != NULL) {
nbr->state = NBR_REACHABLE;
nbr->nscount = 0;
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
}
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
uip_ds6_get_least_lifetime_neighbor(void)
{
@ -340,6 +356,6 @@ uip_ds6_get_least_lifetime_neighbor(void)
}
return nbr_expiring;
}
#endif /* UIP_ND6_SEND_NA */
#endif /* UIP_ND6_SEND_NS */
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -69,11 +69,11 @@ typedef struct uip_ds6_nbr {
uip_ipaddr_t ipaddr;
uint8_t isrouter;
uint8_t state;
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
#if UIP_ND6_SEND_NS || UIP_ND6_SEND_RA
struct stimer reachable;
struct stimer sendns;
uint8_t nscount;
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
#endif /* UIP_ND6_SEND_NS || UIP_ND6_SEND_RA */
#if UIP_CONF_IPV6_QUEUE_PKT
struct uip_packetqueue_handle packethandle;
#define UIP_DS6_NBR_PACKET_LIFETIME CLOCK_SECOND * 4
@ -98,6 +98,17 @@ void uip_ds6_link_neighbor_callback(int status, int numtx);
void uip_ds6_neighbor_periodic(void);
int uip_ds6_nbr_num(void);
#if UIP_ND6_SEND_NS
/**
* \brief Refresh the reachable state of a neighbor. This function
* may be called when a node receives an IPv6 message that confirms the
* reachability of a neighbor.
* \param ipaddr pointer to the IPv6 address whose neighbor reachability state
* should be refreshed.
*/
void uip_ds6_nbr_refresh_reachable_state(const uip_ipaddr_t *ipaddr);
#endif /* UIP_ND6_SEND_NS */
/**
* \brief
* This searches inside the neighbor table for the neighbor that is about to

View file

@ -573,6 +573,12 @@ uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop)
}
/*---------------------------------------------------------------------------*/
uip_ds6_defrt_t *
uip_ds6_defrt_head(void)
{
return list_head(defaultrouterlist);
}
/*---------------------------------------------------------------------------*/
uip_ds6_defrt_t *
uip_ds6_defrt_add(uip_ipaddr_t *ipaddr, unsigned long interval)
{
uip_ds6_defrt_t *d;

View file

@ -177,6 +177,7 @@ typedef struct uip_ds6_defrt {
/** \name Default router list basic routines */
/** @{ */
uip_ds6_defrt_t *uip_ds6_defrt_head(void);
uip_ds6_defrt_t *uip_ds6_defrt_add(uip_ipaddr_t *ipaddr,
unsigned long interval);
void uip_ds6_defrt_rm(uip_ds6_defrt_t *defrt);

View file

@ -187,9 +187,9 @@ uip_ds6_periodic(void)
}
#endif /* !UIP_CONF_ROUTER */
#if UIP_ND6_SEND_NA
#if UIP_ND6_SEND_NS
uip_ds6_neighbor_periodic();
#endif /* UIP_ND6_SEND_RA */
#endif /* UIP_ND6_SEND_NS */
#if UIP_CONF_ROUTER && UIP_ND6_SEND_RA
/* Periodic RA sending */

View file

@ -116,13 +116,16 @@ void uip_log(char *msg);
#define UIP_ND6_OPT_RDNSS_BUF ((uip_nd6_opt_dns *)&uip_buf[uip_l2_l3_icmp_hdr_len + nd6_opt_offset])
/** @} */
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
#if UIP_ND6_SEND_NS || UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
static uint8_t nd6_opt_offset; /** Offset from the end of the icmpv6 header to the option in uip_buf*/
static uint8_t *nd6_opt_llao; /** Pointer to llao option in uip_buf */
static uip_ds6_nbr_t *nbr; /** Pointer to a nbr cache entry*/
static uip_ds6_defrt_t *defrt; /** Pointer to a router list entry */
static uip_ds6_addr_t *addr; /** Pointer to an interface address */
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
#endif /* UIP_ND6_SEND_NS || UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
#if UIP_ND6_SEND_NS || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
static uip_ds6_defrt_t *defrt; /** Pointer to a router list entry */
#endif /* UIP_ND6_SEND_NS || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
#if !UIP_CONF_ROUTER // TBD see if we move it to ra_input
static uip_nd6_opt_prefix_info *nd6_opt_prefix_info; /** Pointer to prefix information option in uip_buf */
@ -157,7 +160,27 @@ create_llao(uint8_t *llao, uint8_t type) {
}
/*------------------------------------------------------------------*/
/**
* Neighbor Solicitation Processing
*
* The NS can be received in 3 cases (procedures):
* - sender is performing DAD (ip src = unspecified, no SLLAO option)
* - sender is performing NUD (ip dst = unicast)
* - sender is performing address resolution (ip dest = solicited node mcast
* address)
*
* We do:
* - if the tgt belongs to me, reply, otherwise ignore
* - if i was performing DAD for the same address, two cases:
* -- I already sent a NS, hence I win
* -- I did not send a NS yet, hence I lose
*
* If we need to send a NA in response (i.e. the NS was done for NUD, or
* address resolution, or DAD and there is a conflict), we do it in this
* function: set src, dst, tgt address in the three cases, then for all cases
* set the rest, including SLLAO
*
*/
#if UIP_ND6_SEND_NA
static void
ns_input(void)
@ -238,9 +261,9 @@ ns_input(void)
addr = uip_ds6_addr_lookup(&UIP_ND6_NS_BUF->tgtipaddr);
if(addr != NULL) {
#if UIP_ND6_DEF_MAXDADNS > 0
if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) {
/* DAD CASE */
#if UIP_ND6_DEF_MAXDADNS > 0
#if UIP_CONF_IPV6_CHECKS
if(!uip_is_addr_solicited_node(&UIP_IP_BUF->destipaddr)) {
PRINTF("NS received is bad\n");
@ -258,9 +281,7 @@ ns_input(void)
goto discard;
}
#else /* UIP_ND6_DEF_MAXDADNS > 0 */
if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) {
/* DAD CASE */
goto discard;
goto discard; /* DAD CASE */
#endif /* UIP_ND6_DEF_MAXDADNS > 0 */
}
#if UIP_CONF_IPV6_CHECKS
@ -348,6 +369,7 @@ discard:
/*------------------------------------------------------------------*/
#if UIP_ND6_SEND_NS
void
uip_nd6_ns_output(uip_ipaddr_t * src, uip_ipaddr_t * dest, uip_ipaddr_t * tgt)
{
@ -410,7 +432,9 @@ uip_nd6_ns_output(uip_ipaddr_t * src, uip_ipaddr_t * dest, uip_ipaddr_t * tgt)
PRINTF("\n");
return;
}
#if UIP_ND6_SEND_NA
#endif /* UIP_ND6_SEND_NS */
#if UIP_ND6_SEND_NS
/*------------------------------------------------------------------*/
/**
* Neighbor Advertisement Processing
@ -522,14 +546,11 @@ na_input(void)
goto discard;
}
if(is_solicited) {
nbr->state = NBR_REACHABLE;
nbr->nscount = 0;
/* reachable time is stored in ms */
stimer_set(&(nbr->reachable), uip_ds6_if.reachable_time / 1000);
} else {
/* Note: No need to refresh the state of the nbr here.
* It has already been refreshed upon receiving the unicast IPv6 ND packet.
* See: uip_ds6_nbr_refresh_reachable_state()
*/
if(!is_solicited) {
nbr->state = NBR_STALE;
}
nbr->isrouter = is_router;
@ -552,11 +573,10 @@ na_input(void)
goto discard;
}
}
if(is_solicited) {
nbr->state = NBR_REACHABLE;
/* reachable time is stored in ms */
stimer_set(&(nbr->reachable), uip_ds6_if.reachable_time / 1000);
}
/* Note: No need to refresh the state of the nbr here.
* It has already been refreshed upon receiving the unicast IPv6 ND packet.
* See: uip_ds6_nbr_refresh_reachable_state()
*/
}
}
if(nbr->isrouter && !is_router) {
@ -589,7 +609,7 @@ discard:
uip_clear_buf();
return;
}
#endif /* UIP_ND6_SEND_NA */
#endif /* UIP_ND6_SEND_NS */
#if UIP_CONF_ROUTER
#if UIP_ND6_SEND_RA
@ -1083,6 +1103,8 @@ discard:
#if UIP_ND6_SEND_NA
UIP_ICMP6_HANDLER(ns_input_handler, ICMP6_NS, UIP_ICMP6_HANDLER_CODE_ANY,
ns_input);
#endif
#if UIP_ND6_SEND_NS
UIP_ICMP6_HANDLER(na_input_handler, ICMP6_NA, UIP_ICMP6_HANDLER_CODE_ANY,
na_input);
#endif
@ -1100,19 +1122,16 @@ UIP_ICMP6_HANDLER(ra_input_handler, ICMP6_RA, UIP_ICMP6_HANDLER_CODE_ANY,
void
uip_nd6_init()
{
#if UIP_ND6_SEND_NA
/* Only handle NSs if we are prepared to send out NAs */
uip_icmp6_register_input_handler(&ns_input_handler);
/*
* Only handle NAs if we are prepared to send out NAs.
* This is perhaps logically incorrect, but this condition was present in
* uip_process and we keep it until proven wrong
*/
uip_icmp6_register_input_handler(&na_input_handler);
#endif
#if UIP_ND6_SEND_NS
/*
* Only handle NAs if we are prepared to send out NSs. */
uip_icmp6_register_input_handler(&na_input_handler);
#endif
#if UIP_CONF_ROUTER && UIP_ND6_SEND_RA
/* Only accept RS if we are a router and happy to send out RAs */

View file

@ -60,11 +60,23 @@
/** \name RFC 4861 Host constant */
/** @{ */
/** \brief Maximum router solicitation delay */
#ifndef UIP_CONF_ND6_MAX_RTR_SOLICITATION_DELAY
#define UIP_ND6_MAX_RTR_SOLICITATION_DELAY 1
#else
#define UIP_ND6_MAX_RTR_SOLICITATION_DELAY UIP_CONF_ND6_MAX_RTR_SOLICITATION_DELAY
#endif
/** \brief Router solicitation interval */
#ifndef UIP_CONF_ND6_RTR_SOLICITATION_INTERVAL
#define UIP_ND6_RTR_SOLICITATION_INTERVAL 4
#else
#define UIP_ND6_RTR_SOLICITATION_INTERVAL UIP_CONF_ND6_RTR_SOLICITATION_INTERVAL
#endif
/** \brief Maximum router solicitations */
#ifndef UIP_CONF_ND6_MAX_RTR_SOLICITATIONS
#define UIP_ND6_MAX_RTR_SOLICITATIONS 3
#else
#define UIP_ND6_MAX_RTR_SOLICITATIONS UIP_CONF_ND6_MAX_RTR_SOLICITATIONS
#endif
/** @} */
/** \name RFC 4861 Router constants */
@ -74,6 +86,11 @@
#else
#define UIP_ND6_SEND_RA UIP_CONF_ND6_SEND_RA
#endif
#ifndef UIP_CONF_ND6_SEND_NS
#define UIP_ND6_SEND_NS 1 /* enable/disable NS sending */
#else
#define UIP_ND6_SEND_NS UIP_CONF_ND6_SEND_NS
#endif
#ifndef UIP_CONF_ND6_SEND_NA
#define UIP_ND6_SEND_NA 1 /* enable/disable NA sending */
#else
@ -91,7 +108,11 @@
#endif
#define UIP_ND6_M_FLAG 0
#define UIP_ND6_O_FLAG (UIP_ND6_RA_RDNSS || UIP_ND6_RA_DNSSL)
#ifndef UIP_CONF_ROUTER_LIFETIME
#define UIP_ND6_ROUTER_LIFETIME 3 * UIP_ND6_MAX_RA_INTERVAL
#else
#define UIP_ND6_ROUTER_LIFETIME UIP_CONF_ROUTER_LIFETIME
#endif
#define UIP_ND6_MAX_INITIAL_RA_INTERVAL 16 /*seconds*/
#define UIP_ND6_MAX_INITIAL_RAS 3 /*transmissions*/
@ -109,7 +130,7 @@
#if UIP_CONF_LL_802154
#define UIP_ND6_DEF_MAXDADNS 0
#else /* UIP_CONF_LL_802154 */
#define UIP_ND6_DEF_MAXDADNS UIP_ND6_SEND_NA
#define UIP_ND6_DEF_MAXDADNS UIP_ND6_SEND_NS
#endif /* UIP_CONF_LL_802154 */
#else /* UIP_CONF_ND6_DEF_MAXDADNS */
#define UIP_ND6_DEF_MAXDADNS UIP_CONF_ND6_DEF_MAXDADNS
@ -336,30 +357,6 @@ typedef struct uip_nd6_opt_redirected_hdr {
* @{
*/
/**
* \brief Process a neighbor solicitation
*
* The NS can be received in 3 cases (procedures):
* - sender is performing DAD (ip src = unspecified, no SLLAO option)
* - sender is performing NUD (ip dst = unicast)
* - sender is performing address resolution (ip dest = solicited node mcast
* address)
*
* We do:
* - if the tgt belongs to me, reply, otherwise ignore
* - if i was performing DAD for the same address, two cases:
* -- I already sent a NS, hence I win
* -- I did not send a NS yet, hence I lose
*
* If we need to send a NA in response (i.e. the NS was done for NUD, or
* address resolution, or DAD and there is a conflict), we do it in this
* function: set src, dst, tgt address in the three cases, then for all cases
* set the rest, including SLLAO
*
*/
void
uip_nd6_ns_input(void);
/**
* \brief Send a neighbor solicitation, send a Neighbor Advertisement
* \param src pointer to the src of the NS if known
* \param dest pointer to ip address to send the NS, for DAD or ADDR Resol,

View file

@ -85,6 +85,10 @@
#include "rpl/rpl-private.h"
#endif
#if UIP_ND6_SEND_NS
#include "net/ipv6/uip-ds6-nbr.h"
#endif /* UIP_ND6_SEND_NS */
#include <string.h>
/*---------------------------------------------------------------------------*/
@ -1151,6 +1155,13 @@ uip_process(uint8_t flag)
goto drop;
}
/* Refresh neighbor state after receiving a unicast message */
#if UIP_ND6_SEND_NS
if(!uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
uip_ds6_nbr_refresh_reachable_state(&UIP_IP_BUF->srcipaddr);
}
#endif /* UIP_ND6_SEND_NS */
#if UIP_CONF_ROUTER
/*
* Next header field processing. In IPv6, we can have extension headers,

View file

@ -0,0 +1,347 @@
/*
* Copyright (c) 2014, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "websocket-http-client.h"
#include "net/ip/uiplib.h"
#include "net/ip/resolv.h"
#include "ip64-addr.h"
#include <stdio.h>
#include <string.h>
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
enum {
STATE_WAITING_FOR_HEADER,
STATE_WAITING_FOR_CONNECTED,
STATE_STEADY_STATE,
};
/*---------------------------------------------------------------------------*/
static void
send_get(struct websocket_http_client_state *s)
{
struct tcp_socket *tcps;
tcps = &s->s;
tcp_socket_send_str(tcps, "GET ");
tcp_socket_send_str(tcps, s->file);
tcp_socket_send_str(tcps, " HTTP/1.1\r\n");
tcp_socket_send_str(tcps, "Host: ");
tcp_socket_send_str(tcps, s->host);
tcp_socket_send_str(tcps, "\r\n");
if(strlen(s->header) > 0) {
tcp_socket_send_str(tcps, s->header);
}
/* The Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== header is
supposed to be a random value, encoded as base64, that is SHA1
hashed by the server and returned in a HTTP header. This is used
to make sure that we are not seeing some cached version of this
conversation. But we have no SHA1 code by default in Contiki, so
we can't check the return value. Therefore we just use a
hardcoded value here. */
tcp_socket_send_str(tcps,
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Protocol:");
tcp_socket_send_str(tcps, s->subprotocol);
tcp_socket_send_str(tcps, "\r\n");
tcp_socket_send_str(tcps, "\r\n");
PRINTF("websocket-http-client: send_get(): output buffer left %d\n", tcp_socket_max_sendlen(tcps));
}
/*---------------------------------------------------------------------------*/
static void
send_connect(struct websocket_http_client_state *s)
{
struct tcp_socket *tcps;
char buf[20];
tcps = &s->s;
tcp_socket_send_str(tcps, "CONNECT ");
tcp_socket_send_str(tcps, s->host);
tcp_socket_send_str(tcps, ":");
sprintf(buf, "%d", s->port);
tcp_socket_send_str(tcps, buf);
tcp_socket_send_str(tcps, " HTTP/1.1\r\n");
tcp_socket_send_str(tcps, "Host: ");
tcp_socket_send_str(tcps, s->host);
tcp_socket_send_str(tcps, "\r\n");
tcp_socket_send_str(tcps, "Proxy-Connection: Keep-Alive\r\n\r\n");
}
/*---------------------------------------------------------------------------*/
static void
event(struct tcp_socket *tcps, void *ptr,
tcp_socket_event_t e)
{
struct websocket_http_client_state *s = ptr;
if(e == TCP_SOCKET_CONNECTED) {
if(s->proxy_port != 0) {
send_connect(s);
} else {
send_get(s);
}
} else if(e == TCP_SOCKET_CLOSED) {
websocket_http_client_closed(s);
} else if(e == TCP_SOCKET_TIMEDOUT) {
websocket_http_client_timedout(s);
} else if(e == TCP_SOCKET_ABORTED) {
websocket_http_client_aborted(s);
} else if(e == TCP_SOCKET_DATA_SENT) {
/* We could feed this information up to the websocket.c layer, but
we currently do not do that. */
}
}
/*---------------------------------------------------------------------------*/
static int
parse_header_byte(struct websocket_http_client_state *s,
uint8_t b)
{
static const char *endmarker = "\r\n\r\n";
PT_BEGIN(&s->parse_header_pt);
/* Skip the first part of the HTTP response */
while(b != ' ') {
PT_YIELD(&s->parse_header_pt);
}
/* Skip the space that follow the first part */
PT_YIELD(&s->parse_header_pt);
/* Read the first three bytes that constistute the HTTP status
code. We store the HTTP status code as an integer in the
s->http_status field. */
s->http_status = (b - '0');
PT_YIELD(&s->parse_header_pt);
s->http_status = s->http_status * 10 + (b - '0');
PT_YIELD(&s->parse_header_pt);
s->http_status = s->http_status * 10 + (b - '0');
if((s->proxy_port != 0 && !(s->http_status == 200 || s->http_status == 101)) ||
(s->proxy_port == 0 && s->http_status != 101)) {
/* This is a websocket request, so the server should have answered
with a 101 Switching protocols response. */
PRINTF("Websocket HTTP client didn't get the 101 status code (got %d), closing connection\n",
s->http_status);
websocket_http_client_close(s);
while(1) {
PT_YIELD(&s->parse_header_pt);
}
}
/* Keep eating header bytes until we reach the end of it. The end is
indicated by the string "\r\n\r\n". We don't actually look at any
of the headers.
The s->i variable contains the number of consecutive bytes
matched. If we match the total length of the string, we stop.
*/
s->i = 0;
do {
PT_YIELD(&s->parse_header_pt);
if(b == (uint8_t)endmarker[s->i]) {
s->i++;
} else {
s->i = 0;
}
} while(s->i < strlen(endmarker));
if(s->proxy_port != 0 && s->state == STATE_WAITING_FOR_HEADER) {
send_get(s);
s->state = STATE_WAITING_FOR_CONNECTED;
} else {
s->state = STATE_STEADY_STATE;
websocket_http_client_connected(s);
}
PT_END(&s->parse_header_pt);
}
/*---------------------------------------------------------------------------*/
static int
input(struct tcp_socket *tcps, void *ptr,
const uint8_t *inputptr, int inputdatalen)
{
struct websocket_http_client_state *s = ptr;
if(s->state == STATE_WAITING_FOR_HEADER ||
s->state == STATE_WAITING_FOR_CONNECTED) {
int i;
for(i = 0; i < inputdatalen; i++) {
parse_header_byte(s, inputptr[i]);
if(s->state == STATE_STEADY_STATE) {
i++;
break;
}
}
if(i < inputdatalen && s->state == STATE_STEADY_STATE) {
websocket_http_client_datahandler(s, &inputptr[i], inputdatalen - i);
}
} else {
websocket_http_client_datahandler(s, inputptr, inputdatalen);
}
return 0; /* all data consumed */
}
/*---------------------------------------------------------------------------*/
int
websocket_http_client_register(struct websocket_http_client_state *s,
const char *host,
uint16_t port,
const char *file,
const char *subprotocol,
const char *header)
{
if(host == NULL) {
return -1;
}
strncpy(s->host, host, sizeof(s->host));
if(file == NULL) {
return -1;
}
strncpy(s->file, file, sizeof(s->file));
if(subprotocol == NULL) {
return -1;
}
strncpy(s->subprotocol, subprotocol, sizeof(s->subprotocol));
if(header == NULL) {
strncpy(s->header, "", sizeof(s->header));
} else {
strncpy(s->header, header, sizeof(s->header));
}
if(port == 0) {
s->port = 80;
} else {
s->port = port;
}
return 1;
}
/*---------------------------------------------------------------------------*/
int
websocket_http_client_get(struct websocket_http_client_state *s)
{
uip_ip4addr_t ip4addr;
uip_ip6addr_t ip6addr;
uip_ip6addr_t *addr;
uint16_t port;
PRINTF("websocket_http_client_get: connecting to %s with file %s subprotocol %s header %s\n",
s->host, s->file, s->subprotocol, s->header);
s->state = STATE_WAITING_FOR_HEADER;
if(tcp_socket_register(&s->s, s,
s->inputbuf, sizeof(s->inputbuf),
s->outputbuf, sizeof(s->outputbuf),
input, event) < 0) {
return -1;
}
port = s->port;
if(s->proxy_port != 0) {
/* The proxy address should be an IPv6 address. */
uip_ipaddr_copy(&ip6addr, &s->proxy_addr);
port = s->proxy_port;
} else if(uiplib_ip6addrconv(s->host, &ip6addr) == 0) {
/* First check if the host is an IP address. */
if(uiplib_ip4addrconv(s->host, &ip4addr) != 0) {
ip64_addr_4to6(&ip4addr, &ip6addr);
} else {
/* Try to lookup the hostname. If it fails, we initiate a hostname
lookup. */
if(resolv_lookup(s->host, &addr) != RESOLV_STATUS_CACHED) {
return -1;
}
return tcp_socket_connect(&s->s, addr, s->port);
}
}
return tcp_socket_connect(&s->s, &ip6addr, port);
}
/*---------------------------------------------------------------------------*/
int
websocket_http_client_send(struct websocket_http_client_state *s,
const uint8_t *data,
uint16_t datalen)
{
if(s->state == STATE_STEADY_STATE) {
return tcp_socket_send(&s->s, data, datalen);
}
return -1;
}
/*---------------------------------------------------------------------------*/
int
websocket_http_client_sendbuflen(struct websocket_http_client_state *s)
{
return tcp_socket_max_sendlen(&s->s);
}
/*---------------------------------------------------------------------------*/
void
websocket_http_client_close(struct websocket_http_client_state *s)
{
tcp_socket_close(&s->s);
}
/*---------------------------------------------------------------------------*/
const char *
websocket_http_client_hostname(struct websocket_http_client_state *s)
{
return s->host;
}
/*---------------------------------------------------------------------------*/
void
websocket_http_client_init(struct websocket_http_client_state *s)
{
uip_create_unspecified(&s->proxy_addr);
s->proxy_port = 0;
}
/*---------------------------------------------------------------------------*/
void
websocket_http_client_set_proxy(struct websocket_http_client_state *s,
const uip_ipaddr_t *addr, uint16_t port)
{
uip_ipaddr_copy(&s->proxy_addr, addr);
s->proxy_port = port;
}
/*---------------------------------------------------------------------------*/
int
websocket_http_client_queuelen(struct websocket_http_client_state *s)
{
return tcp_socket_queuelen(&s->s);
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2014, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKET_HTTP_CLIENT_H_
#define WEBSOCKET_HTTP_CLIENT_H_
#include "contiki.h"
#include "tcp-socket.h"
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE
#define WEBSOCKET_HTTP_CLIENT_INPUTBUFSIZE WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE
#else /* WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE */
#define WEBSOCKET_HTTP_CLIENT_INPUTBUFSIZE 100
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE */
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE
#define WEBSOCKET_HTTP_CLIENT_OUTPUTBUFSIZE WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE
#else /* WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE */
#define WEBSOCKET_HTTP_CLIENT_OUTPUTBUFSIZE 300
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE */
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN
#define WEBSOCKET_HTTP_CLIENT_MAX_HOSTLEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN */
#define WEBSOCKET_HTTP_CLIENT_MAX_HOSTLEN 32
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN */
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN
#define WEBSOCKET_HTTP_CLIENT_MAX_FILELEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN */
#define WEBSOCKET_HTTP_CLIENT_MAX_FILELEN 32
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN */
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN
#define WEBSOCKET_HTTP_CLIENT_MAX_SUBPROTOCOLLEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN */
#define WEBSOCKET_HTTP_CLIENT_MAX_SUBPROTOCOLLEN 24
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN */
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN
#define WEBSOCKET_HTTP_CLIENT_MAX_HEADERLEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN */
#define WEBSOCKET_HTTP_CLIENT_MAX_HEADERLEN 128
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN */
struct websocket_http_client_state {
struct tcp_socket s;
uint8_t inputbuf[WEBSOCKET_HTTP_CLIENT_INPUTBUFSIZE];
uint8_t outputbuf[WEBSOCKET_HTTP_CLIENT_OUTPUTBUFSIZE];
char host[WEBSOCKET_HTTP_CLIENT_MAX_HOSTLEN];
char file[WEBSOCKET_HTTP_CLIENT_MAX_FILELEN];
char subprotocol[WEBSOCKET_HTTP_CLIENT_MAX_SUBPROTOCOLLEN];
char header[WEBSOCKET_HTTP_CLIENT_MAX_HEADERLEN];
uint16_t port;
int state;
struct pt parse_header_pt;
int http_status;
int i;
uip_ipaddr_t proxy_addr;
uint16_t proxy_port;
};
void websocket_http_client_init(struct websocket_http_client_state *s);
void websocket_http_client_set_proxy(struct websocket_http_client_state *s,
const uip_ipaddr_t *addr, uint16_t port);
int websocket_http_client_register(struct websocket_http_client_state *s,
const char *host,
uint16_t port,
const char *file,
const char *subprotocol,
const char *hdr);
int websocket_http_client_get(struct websocket_http_client_state *s);
int websocket_http_client_send(struct websocket_http_client_state *s,
const uint8_t *data,
uint16_t datalen);
int websocket_http_client_sendbuflen(struct websocket_http_client_state *s);
void websocket_http_client_close(struct websocket_http_client_state *s);
const char *websocket_http_client_hostname(struct websocket_http_client_state *s);
int websocket_http_client_queuelen(struct websocket_http_client_state *s);
/* Callback functions that have to be implemented by the application
program. */
void websocket_http_client_datahandler(struct websocket_http_client_state *s,
const uint8_t *data, uint16_t len);
void websocket_http_client_connected(struct websocket_http_client_state *s);
void websocket_http_client_timedout(struct websocket_http_client_state *s);
void websocket_http_client_aborted(struct websocket_http_client_state *s);
void websocket_http_client_closed(struct websocket_http_client_state *s);
#endif /* WEBSOCKET_HTTP_CLIENT_H_ */

724
core/net/ipv6/websocket.c Normal file
View file

@ -0,0 +1,724 @@
/*
* Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "contiki-net.h"
#include "lib/petsciiconv.h"
#include "websocket.h"
PROCESS(websocket_process, "Websockets process");
#define MAX_HOSTLEN 64
#define MAX_PATHLEN 100
LIST(websocketlist);
#define WEBSOCKET_FIN_BIT 0x80
#define WEBSOCKET_OPCODE_MASK 0x0f
#define WEBSOCKET_OPCODE_CONT 0x00
#define WEBSOCKET_OPCODE_TEXT 0x01
#define WEBSOCKET_OPCODE_BIN 0x02
#define WEBSOCKET_OPCODE_CLOSE 0x08
#define WEBSOCKET_OPCODE_PING 0x09
#define WEBSOCKET_OPCODE_PONG 0x0a
#define WEBSOCKET_MASK_BIT 0x80
#define WEBSOCKET_LEN_MASK 0x7f
struct websocket_frame_hdr {
uint8_t opcode;
uint8_t len;
uint8_t extlen[4];
};
struct websocket_frame_mask {
uint8_t mask[4];
};
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
/*---------------------------------------------------------------------------*/
static int
parse_url(const char *url, char *host, uint16_t *portptr, char *path)
{
const char *urlptr;
int i;
const char *file;
uint16_t port;
if(url == NULL) {
return 0;
}
/* Don't even try to go further if the URL is empty. */
if(strlen(url) == 0) {
return 0;
}
/* See if the URL starts with http:// or ws:// and remove it. */
if(strncmp(url, "http://", strlen("http://")) == 0) {
urlptr = url + strlen("http://");
} else if(strncmp(url, "ws://", strlen("ws://")) == 0) {
urlptr = url + strlen("ws://");
} else {
urlptr = url;
}
/* Find host part of the URL. */
for(i = 0; i < MAX_HOSTLEN; ++i) {
if(*urlptr == 0 ||
*urlptr == '/' ||
*urlptr == ' ' ||
*urlptr == ':') {
if(host != NULL) {
host[i] = 0;
}
break;
}
if(host != NULL) {
host[i] = *urlptr;
}
++urlptr;
}
/* Find the port. Default is 0, which lets the underlying transport
select its default port. */
port = 0;
if(*urlptr == ':') {
port = 0;
do {
++urlptr;
if(*urlptr >= '0' && *urlptr <= '9') {
port = (10 * port) + (*urlptr - '0');
}
} while(*urlptr >= '0' &&
*urlptr <= '9');
}
if(portptr != NULL) {
*portptr = port;
}
/* Find file part of the URL. */
while(*urlptr != '/' && *urlptr != 0) {
++urlptr;
}
if(*urlptr == '/') {
file = urlptr;
} else {
file = "/";
}
if(path != NULL) {
strncpy(path, file, MAX_PATHLEN);
}
return 1;
}
/*---------------------------------------------------------------------------*/
static int
start_get(struct websocket *s)
{
if(websocket_http_client_get(&(s->s)) == 0) {
PRINTF("Out of memory error\n");
s->state = WEBSOCKET_STATE_CLOSED;
return WEBSOCKET_ERR;
} else {
PRINTF("Connecting...\n");
s->state = WEBSOCKET_STATE_HTTP_REQUEST_SENT;
return WEBSOCKET_OK;
}
return WEBSOCKET_ERR;
}
/*---------------------------------------------------------------------------*/
void
call(struct websocket *s, websocket_result_t r,
const uint8_t *data, uint16_t datalen)
{
if(s != NULL && s->callback != NULL) {
s->callback(s, r, data, datalen);
}
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(websocket_process, ev, data)
{
PROCESS_BEGIN();
while(1) {
PROCESS_WAIT_EVENT();
if(ev == resolv_event_found && data != NULL) {
int ret;
struct websocket *s;
const char *name = data;
/* Either found a hostname, or not. We need to go through the
list of websocketsand figure out to which connection this
reply corresponds, then either restart the HTTP get, or kill
it (if no hostname was found). */
for(s = list_head(websocketlist);
s != NULL;
s = list_item_next(s)) {
if(strcmp(name, websocket_http_client_hostname(&s->s)) == 0) {
ret = resolv_lookup(name, NULL);
if(ret == RESOLV_STATUS_CACHED) {
/* Hostname found, restart get. */
if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
PRINTF("Restarting get\n");
start_get(s);
}
} else {
if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
/* Hostname not found, kill connection. */
/* PRINTF("XXX killing connection\n");*/
call(s, WEBSOCKET_HOSTNAME_NOT_FOUND, NULL, 0);
}
}
}
}
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
/* Callback function. Called from the webclient when the HTTP
* connection was abruptly aborted.
*/
void
websocket_http_client_aborted(struct websocket_http_client_state *client_state)
{
if(client_state != NULL) {
struct websocket *s = (struct websocket *)
((char *)client_state - offsetof(struct websocket, s));
PRINTF("Websocket reset\n");
s->state = WEBSOCKET_STATE_CLOSED;
call(s, WEBSOCKET_RESET, NULL, 0);
}
}
/*---------------------------------------------------------------------------*/
/* Callback function. Called from the webclient when the HTTP
* connection timed out.
*/
void
websocket_http_client_timedout(struct websocket_http_client_state *client_state)
{
if(client_state != NULL) {
struct websocket *s = (struct websocket *)
((char *)client_state - offsetof(struct websocket, s));
PRINTF("Websocket timed out\n");
s->state = WEBSOCKET_STATE_CLOSED;
call(s, WEBSOCKET_TIMEDOUT, NULL, 0);
}
}
/*---------------------------------------------------------------------------*/
/* Callback function. Called from the webclient when the HTTP
* connection was closed after a request from the "websocket_http_client_close()"
* function. .
*/
void
websocket_http_client_closed(struct websocket_http_client_state *client_state)
{
if(client_state != NULL) {
struct websocket *s = (struct websocket *)
((char *)client_state - offsetof(struct websocket, s));
PRINTF("Websocket closed.\n");
s->state = WEBSOCKET_STATE_CLOSED;
call(s, WEBSOCKET_CLOSED, NULL, 0);
}
}
/*---------------------------------------------------------------------------*/
/* Callback function. Called from the webclient when the HTTP
* connection is connected.
*/
void
websocket_http_client_connected(struct websocket_http_client_state *client_state)
{
struct websocket *s = (struct websocket *)
((char *)client_state - offsetof(struct websocket, s));
PRINTF("Websocket connected\n");
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
call(s, WEBSOCKET_CONNECTED, NULL, 0);
}
/*---------------------------------------------------------------------------*/
/* The websocket header may potentially be split into multiple TCP
segments. This function eats one byte each, puts it into
s->headercache, and checks whether or not the full header has been
received. */
static int
receive_header_byte(struct websocket *s, uint8_t byte)
{
int len;
int expected_len;
struct websocket_frame_hdr *hdr;
/* Take the next byte of data and place it in the header cache. */
if(s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
s->headercache[s->headercacheptr] = byte;
s->headercacheptr++;
if(s->headercacheptr >= sizeof(s->headercache)) {
/* Something bad happened: we ad read 10 bytes and had not yet
found a reasonable header, so we close the socket. */
websocket_close(s);
}
}
len = s->headercacheptr;
hdr = (struct websocket_frame_hdr *)s->headercache;
/* Check the header that we have received to see if it is long
enough. */
/* We start with expecting a length of at least two bytes (opcode +
1 length byte). */
expected_len = 2;
if(len >= expected_len) {
/* We check how many more bytes we should expect to see. The
length byte determines how many length bytes are included in
the header. */
if((hdr->len & WEBSOCKET_LEN_MASK) == 126) {
expected_len += 2;
} else if((hdr->len & WEBSOCKET_LEN_MASK) == 127) {
expected_len += 4;
}
/* If the option has the mask bit set, we should expect to see 4
mask bytes at the end of the header. */
if((hdr->len & WEBSOCKET_MASK_BIT ) != 0) {
expected_len += 4;
}
/* Now we know how long our header if expected to be. If it is
this long, we are done and we set the state to reflect this. */
if(len == expected_len) {
s->state = WEBSOCKET_STATE_HEADER_RECEIVED;
return 1;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
/* Callback function. Called from the webclient module when HTTP data
* has arrived.
*/
void
websocket_http_client_datahandler(struct websocket_http_client_state *client_state,
const uint8_t *data, uint16_t datalen)
{
struct websocket *s = (struct websocket *)
((char *)client_state - offsetof(struct websocket, s));
struct websocket_frame_hdr *hdr;
struct websocket_frame_mask *maskptr;
if(data == NULL) {
call(s, WEBSOCKET_CLOSED, NULL, 0);
} else {
/* This function is a state machine that does different things
depending on the state. If we are waiting for header (the
default state), we change to the RECEIVING_HEADER state when we
get the first byte. If we are receiving header, we put all
bytes we have into a header buffer until the full header has
been received. If we have received the header, we parse it. If
we have received and parsed the header, we are ready to receive
data. Finally, if there is data left in the incoming packet, we
repeat the process. */
if(s->state == WEBSOCKET_STATE_WAITING_FOR_HEADER) {
s->state = WEBSOCKET_STATE_RECEIVING_HEADER;
s->headercacheptr = 0;
}
if(s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
while(datalen > 0 && s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
receive_header_byte(s, data[0]);
data++;
datalen--;
}
}
if(s->state == WEBSOCKET_STATE_HEADER_RECEIVED) {
/* If this is the start of an incoming websocket data frame, we
decode the header and check if we should act on in. If not, we
pipe the data to the application through a callback handler. If
data arrives in multiple packets, it is up to the application to
put it back together again. */
/* The websocket header is at the start of the incoming data. */
hdr = (struct websocket_frame_hdr *)s->headercache;
/* The s->left field holds the length of the application data
* chunk that we are about to receive. */
s->len = s->left = 0;
/* The s->mask field holds the bitmask of the data chunk, if
* any. */
memset(s->mask, 0, sizeof(s->mask));
/* We first read out the length of the application data
chunk. The length may be encoded over multiple bytes. If the
length is >= 126 bytes, it is encoded as two or more
bytes. The first length field determines if it is in 2 or 4
bytes. We also keep track of where the bitmask is held - its
place also differs depending on how the length is encoded. */
maskptr = (struct websocket_frame_mask *)hdr->extlen;
if((hdr->len & WEBSOCKET_LEN_MASK) < 126) {
s->len = s->left = hdr->len & WEBSOCKET_LEN_MASK;
} else if(hdr->len == 126) {
s->len = s->left = (hdr->extlen[0] << 8) + hdr->extlen[1];
maskptr = (struct websocket_frame_mask *)&hdr->extlen[2];
} else if(hdr->len == 127) {
s->len = s->left = ((uint32_t)hdr->extlen[0] << 24) +
((uint32_t)hdr->extlen[1] << 16) +
((uint32_t)hdr->extlen[2] << 8) +
hdr->extlen[3];
maskptr = (struct websocket_frame_mask *)&hdr->extlen[4];
}
/* Set user_data to point to the first byte of application data.
See if the application data chunk is masked or not. If it is,
we copy the bitmask into the s->mask field. */
if((hdr->len & WEBSOCKET_MASK_BIT) == 0) {
/* PRINTF("No mask\n");*/
} else {
memcpy(s->mask, &maskptr->mask, sizeof(s->mask));
/* PRINTF("There was a mask, %02x %02x %02x %02x\n",
s->mask[0], s->mask[1], s->mask[2], s->mask[3]);*/
}
/* Remember the opcode of the application chunk, put it in the
* s->opcode field. */
s->opcode = hdr->opcode & WEBSOCKET_OPCODE_MASK;
if(s->opcode == WEBSOCKET_OPCODE_PING) {
/* If the opcode is ping, we change the opcode to a pong, and
* send the data back. */
hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
WEBSOCKET_OPCODE_PONG;
websocket_http_client_send(&s->s, (const uint8_t*)hdr, 2);
if(s->left > 0) {
websocket_http_client_send(&s->s, (const uint8_t*)data, s->left);
}
PRINTF("Got ping\n");
call(s, WEBSOCKET_PINGED, NULL, 0);
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
} else if(s->opcode == WEBSOCKET_OPCODE_PONG) {
/* If the opcode is pong, we call the application to let it
know we got a pong. */
PRINTF("Got pong\n");
call(s, WEBSOCKET_PONG_RECEIVED, NULL, 0);
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
} else if(s->opcode == WEBSOCKET_OPCODE_CLOSE) {
/* If the opcode is a close, we send a close frame back. */
hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
WEBSOCKET_OPCODE_CLOSE;
websocket_http_client_send(&s->s, (const uint8_t*)hdr, 2);
if(s->left > 0) {
websocket_http_client_send(&s->s, (const uint8_t*)data, s->left);
}
PRINTF("websocket: got close, sending close\n");
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
websocket_http_client_close(&s->s);
} else if(s->opcode == WEBSOCKET_OPCODE_BIN ||
s->opcode == WEBSOCKET_OPCODE_TEXT) {
/* If the opcode is bin or text, and there is application
* layer data in the packet, we call the application to
* process it. */
if(s->left > 0) {
s->state = WEBSOCKET_STATE_RECEIVING_DATA;
if(datalen > 0) {
int len;
len = MIN(s->left, datalen);
/* XXX todo: mask if needed. */
call(s, WEBSOCKET_DATA, data, len);
data += len;
s->left -= len;
datalen -= len;
}
}
}
if(s->left == 0) {
call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
/* Need to keep parsing the incoming data to check for more
frames, if the incoming datalen is > than s->left. */
if(datalen > 0) {
PRINTF("XXX 1 again\n");
websocket_http_client_datahandler(client_state,
data, datalen);
}
}
} else if(s->state == WEBSOCKET_STATE_RECEIVING_DATA) {
/* XXX todo: mask if needed. */
/* PRINTF("Calling with s->left %d datalen %d\n",
s->left, datalen);*/
if(datalen > 0) {
if(datalen < s->left) {
call(s, WEBSOCKET_DATA, data, datalen);
s->left -= datalen;
data += datalen;
datalen = 0;
} else {
call(s, WEBSOCKET_DATA, data, s->left);
data += s->left;
datalen -= s->left;
s->left = 0;
}
}
if(s->left == 0) {
call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
/* Need to keep parsing the incoming data to check for more
frames, if the incoming datalen is > than len. */
if(datalen > 0) {
PRINTF("XXX 2 again (datalen %d s->left %d)\n", datalen, (int)s->left);
websocket_http_client_datahandler(client_state,
data, datalen);
}
}
}
}
}
/*---------------------------------------------------------------------------*/
static void
init(void)
{
static uint8_t inited = 0;
if(!inited) {
process_start(&websocket_process, NULL);
list_init(websocketlist);
inited = 1;
}
}
/*---------------------------------------------------------------------------*/
void
websocket_init(struct websocket *s)
{
init();
websocket_http_client_init(&s->s);
}
/*---------------------------------------------------------------------------*/
void
websocket_set_proxy(struct websocket *s,
const uip_ipaddr_t *addr, uint16_t port)
{
websocket_http_client_set_proxy(&s->s, addr, port);
}
/*---------------------------------------------------------------------------*/
websocket_result_t
websocket_open(struct websocket *s, const char *url,
const char *subprotocol, const char *hdr,
websocket_callback c)
{
int ret;
char host[MAX_HOSTLEN];
char path[MAX_PATHLEN];
uint16_t port;
uip_ipaddr_t addr;
init();
if(s == NULL) {
return WEBSOCKET_ERR;
}
if(s->state != WEBSOCKET_STATE_CLOSED) {
PRINTF("websocket_open: closing websocket before opening it again.\n");
websocket_close(s);
}
s->callback = c;
if(parse_url(url, host, &port, path)) {
list_add(websocketlist, s);
websocket_http_client_register(&s->s, host, port, path, subprotocol, hdr);
/* First check if the host is an IP address. */
if(uiplib_ip4addrconv(host, (uip_ip4addr_t *)&addr) == 0 &&
uiplib_ip6addrconv(host, (uip_ip6addr_t *)&addr) == 0) {
/* Try to lookup the hostname. If it fails, we initiate a hostname
lookup and print out an informative message on the
statusbar. */
ret = resolv_lookup(host, NULL);
if(ret != RESOLV_STATUS_CACHED) {
resolv_query(host);
s->state = WEBSOCKET_STATE_DNS_REQUEST_SENT;
PRINTF("Resolving host...\n");
return WEBSOCKET_OK;
}
}
PROCESS_CONTEXT_BEGIN(&websocket_process);
ret = start_get(s);
PROCESS_CONTEXT_END();
return ret;
}
return -1;
}
/*---------------------------------------------------------------------------*/
void
websocket_close(struct websocket *s)
{
websocket_http_client_close(&s->s);
s->state = WEBSOCKET_STATE_CLOSED;
}
/*---------------------------------------------------------------------------*/
static int
send_data(struct websocket *s, const void *data,
uint16_t datalen, uint8_t data_type_opcode)
{
uint8_t buf[WEBSOCKET_MAX_MSGLEN + 4 + 4]; /* The extra + 4 + 4 here
comes from the size of
the websocket framing
header. */
struct websocket_frame_hdr *hdr;
struct websocket_frame_mask *mask;
PRINTF("websocket send data len %d %.*s\n", datalen, datalen, (char *)data);
if(s->state == WEBSOCKET_STATE_CLOSED ||
s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT ||
s->state == WEBSOCKET_STATE_HTTP_REQUEST_SENT) {
/* Trying to send data on a non-connected websocket. */
PRINTF("websocket send fail: not connected\n");
return -1;
}
/* We need to have 4 + 4 additional bytes for the websocket framing
header. */
if(4 + 4 + datalen > websocket_http_client_sendbuflen(&s->s)) {
PRINTF("websocket: too few bytes left (%d left, %d needed)\n",
websocket_http_client_sendbuflen(&s->s),
4 + 4 + datalen);
return -1;
}
if(datalen > sizeof(buf) - 4 - 4) {
PRINTF("websocket: trying to send too large data chunk %d > %d\n",
datalen, sizeof(buf) - 4 - 4);
return -1;
}
hdr = (struct websocket_frame_hdr *)&buf[0];
hdr->opcode = WEBSOCKET_FIN_BIT | data_type_opcode;
/* If the datalen is larger than 125 bytes, we need to send the data
length as two bytes. If the data length would be larger than 64k,
we should send the length as 4 bytes, but since we specify the
datalen as an unsigned 16-bit int, we do not handle the 64k case
here. */
if(datalen > 125) {
/* Data from client must always have the mask bit set, and a data
mask sent right after the header. */
hdr->len = 126 | WEBSOCKET_MASK_BIT;
hdr->extlen[0] = datalen >> 8;
hdr->extlen[1] = datalen & 0xff;
mask = (struct websocket_frame_mask *)&buf[4];
mask->mask[0] =
mask->mask[1] =
mask->mask[2] =
mask->mask[3] = 0;
memcpy(&buf[8], data, datalen);
return websocket_http_client_send(&s->s, buf, 8 + datalen);
} else {
/* Data from client must always have the mask bit set, and a data
mask sent right after the header. */
hdr->len = datalen | WEBSOCKET_MASK_BIT;
mask = (struct websocket_frame_mask *)&buf[2];
mask->mask[0] =
mask->mask[1] =
mask->mask[2] =
mask->mask[3] = 0;
memcpy(&buf[6], data, datalen);
return websocket_http_client_send(&s->s, buf, 6 + datalen);
}
return -1;
}
/*---------------------------------------------------------------------------*/
int
websocket_send_str(struct websocket *s, const char *str)
{
return send_data(s, str, strlen(str), WEBSOCKET_OPCODE_TEXT);
}
/*---------------------------------------------------------------------------*/
int
websocket_send(struct websocket *s, const uint8_t *data,
uint16_t datalen)
{
return send_data(s, data, datalen, WEBSOCKET_OPCODE_BIN);
}
/*---------------------------------------------------------------------------*/
int
websocket_ping(struct websocket *s)
{
uint8_t buf[sizeof(struct websocket_frame_hdr) +
sizeof(struct websocket_frame_mask)];
struct websocket_frame_hdr *hdr;
struct websocket_frame_mask *mask;
/* We need 2 + 4 additional bytes for the websocket framing
header. */
if(2 + 4 > websocket_http_client_sendbuflen(&s->s)) {
return -1;
}
hdr = (struct websocket_frame_hdr *)&buf[0];
mask = (struct websocket_frame_mask *)&buf[2];
hdr->opcode = WEBSOCKET_FIN_BIT | WEBSOCKET_OPCODE_PING;
/* Data from client must always have the mask bit set, and a data
mask sent right after the header. */
hdr->len = 0 | WEBSOCKET_MASK_BIT;
/* XXX: We just set a dummy mask of 0 for now and hope that this
works. */
mask->mask[0] =
mask->mask[1] =
mask->mask[2] =
mask->mask[3] = 0;
websocket_http_client_send(&s->s, buf, 2 + 4);
return 1;
}
/*---------------------------------------------------------------------------*/
int
websocket_queuelen(struct websocket *s)
{
return websocket_http_client_queuelen(&s->s);
}
/*---------------------------------------------------------------------------*/

113
core/net/ipv6/websocket.h Normal file
View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKET_H
#define WEBSOCKET_H
#include "websocket-http-client.h"
typedef enum {
WEBSOCKET_ERR = 0,
WEBSOCKET_OK = 1,
WEBSOCKET_IN_PROGRESS = 2,
WEBSOCKET_HOSTNAME_NOT_FOUND = 3,
WEBSOCKET_CONNECTED = 4,
WEBSOCKET_DATA = 5,
WEBSOCKET_RESET = 6,
WEBSOCKET_TIMEDOUT = 7,
WEBSOCKET_CLOSED = 8,
WEBSOCKET_PINGED = 9,
WEBSOCKET_DATA_RECEIVED = 10,
WEBSOCKET_PONG_RECEIVED = 11,
} websocket_result_t;
struct websocket;
typedef void (* websocket_callback)(struct websocket *s,
websocket_result_t result,
const uint8_t *data,
uint16_t datalen);
#ifdef WEBSOCKET_CONF_MAX_MSGLEN
#define WEBSOCKET_MAX_MSGLEN WEBSOCKET_CONF_MAX_MSGLEN
#else /* WEBSOCKET_CONF_MAX_MSGLEN */
#define WEBSOCKET_MAX_MSGLEN 200
#endif /* WEBSOCKET_CONF_MAX_MSGLEN */
struct websocket {
struct websocket *next; /* Must be first. */
struct websocket_http_client_state s;
websocket_callback callback;
uint8_t mask[4];
uint32_t left, len;
uint8_t opcode;
uint8_t state;
uint8_t headercacheptr;
uint8_t headercache[10]; /* The maximum websocket header + mask is 6
+ 4 bytes long */
};
enum {
WEBSOCKET_STATE_CLOSED = 0,
WEBSOCKET_STATE_DNS_REQUEST_SENT = 1,
WEBSOCKET_STATE_HTTP_REQUEST_SENT = 2,
WEBSOCKET_STATE_WAITING_FOR_HEADER = 3,
WEBSOCKET_STATE_RECEIVING_HEADER = 4,
WEBSOCKET_STATE_HEADER_RECEIVED = 5,
WEBSOCKET_STATE_RECEIVING_DATA = 6,
};
void websocket_init(struct websocket *s);
void websocket_set_proxy(struct websocket *s,
const uip_ipaddr_t *addr, uint16_t port);
websocket_result_t websocket_open(struct websocket *s,
const char *url,
const char *subprotocol,
const char *hdr,
websocket_callback c);
int websocket_send(struct websocket *s,
const uint8_t *data, uint16_t datalen);
int websocket_send_str(struct websocket *s,
const char *strptr);
void websocket_close(struct websocket *s);
int websocket_ping(struct websocket *s);
int websocket_queuelen(struct websocket *s);
#endif /* WEBSOCKET_H */

View file

@ -51,7 +51,7 @@
/* Maximum value for the freshness counter */
#define FRESHNESS_MAX 16
/* Statistics with no update in FRESHNESS_EXPIRATION_TIMEOUT is not fresh */
#define FRESHNESS_EXPIRATION_TIME (10 * 60 * CLOCK_SECOND)
#define FRESHNESS_EXPIRATION_TIME (10 * 60 * (clock_time_t)CLOCK_SECOND)
/* EWMA (exponential moving average) used to maintain statistics over time */
#define EWMA_SCALE 100
@ -206,6 +206,6 @@ void
link_stats_init(void)
{
nbr_table_register(link_stats, NULL);
ctimer_set(&periodic_timer, 60 * CLOCK_SECOND * FRESHNESS_HALF_LIFE,
ctimer_set(&periodic_timer, 60 * (clock_time_t)CLOCK_SECOND * FRESHNESS_HALF_LIFE,
periodic, NULL);
}

View file

@ -52,6 +52,9 @@ const linkaddr_t linkaddr_null = { { 0, 0 } };
#if LINKADDR_SIZE == 8
const linkaddr_t linkaddr_null = { { 0, 0, 0, 0, 0, 0, 0, 0 } };
#endif /*LINKADDR_SIZE == 8*/
#if LINKADDR_SIZE == 6
const linkaddr_t linkaddr_null = { { 0, 0, 0, 0, 0, 0 } };
#endif /*LINKADDR_SIZE == 6*/
#endif /*LINKADDR_SIZE == 2*/

View file

@ -207,13 +207,13 @@ static int we_are_receiving_burst = 0;
#define INTER_PACKET_INTERVAL RTIMER_ARCH_SECOND / 2500
#endif
/* AFTER_ACK_DETECTECT_WAIT_TIME is the time to wait after a potential
/* AFTER_ACK_DETECTED_WAIT_TIME is the time to wait after a potential
ACK packet has been detected until we can read it out from the
radio. */
#ifdef CONTIKIMAC_CONF_AFTER_ACK_DETECTECT_WAIT_TIME
#define AFTER_ACK_DETECTECT_WAIT_TIME CONTIKIMAC_CONF_AFTER_ACK_DETECTECT_WAIT_TIME
#ifdef CONTIKIMAC_CONF_AFTER_ACK_DETECTED_WAIT_TIME
#define AFTER_ACK_DETECTED_WAIT_TIME CONTIKIMAC_CONF_AFTER_ACK_DETECTED_WAIT_TIME
#else
#define AFTER_ACK_DETECTECT_WAIT_TIME RTIMER_ARCH_SECOND / 1500
#define AFTER_ACK_DETECTED_WAIT_TIME RTIMER_ARCH_SECOND / 1500
#endif
/* MAX_PHASE_STROBE_TIME is the time that we transmit repeated packets
@ -766,7 +766,7 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr,
NETSTACK_RADIO.channel_clear() == 0)) {
uint8_t ackbuf[ACK_LEN];
wt = RTIMER_NOW();
while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + AFTER_ACK_DETECTECT_WAIT_TIME)) { }
while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + AFTER_ACK_DETECTED_WAIT_TIME)) { }
len = NETSTACK_RADIO.read(ackbuf, ACK_LEN);
if(len == ACK_LEN && seqno == ackbuf[ACK_LEN - 1]) {
@ -878,6 +878,10 @@ qsend_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list)
if(next != NULL) {
packetbuf_set_attr(PACKETBUF_ATTR_PENDING, 1);
}
#if !NETSTACK_CONF_BRIDGE_MODE
/* If NETSTACK_CONF_BRIDGE_MODE is set, assume PACKETBUF_ADDR_SENDER is already set. */
packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
#endif
packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1);
if(NETSTACK_FRAMER.create() < 0) {
PRINTF("contikimac: framer failed\n");

View file

@ -231,10 +231,12 @@ tx_done(int status, struct rdc_buf_list *q, struct neighbor_queue *n)
mac_callback_t sent;
struct qbuf_metadata *metadata;
void *cptr;
uint8_t ntx;
metadata = (struct qbuf_metadata *)q->ptr;
sent = metadata->sent;
cptr = metadata->cptr;
ntx = n->transmissions;
switch(status) {
case MAC_TX_OK:
@ -251,7 +253,7 @@ tx_done(int status, struct rdc_buf_list *q, struct neighbor_queue *n)
}
free_packet(n, q, status);
mac_call_sent_callback(sent, cptr, status, n->transmissions);
mac_call_sent_callback(sent, cptr, status, ntx);
}
/*---------------------------------------------------------------------------*/
static void

View file

@ -146,24 +146,45 @@ frame802154_has_panid(frame802154_fcf_t *fcf, int *has_src_pan_id, int *has_dest
}
if(fcf->frame_version == FRAME802154_IEEE802154E_2012) {
/* IEEE 802.15.4e-2012, Table 2a, PAN ID Compression */
if(!fcf->panid_compression) {
if(fcf->dest_addr_mode) {
/* Use destination PAN ID if destination address is present */
dest_pan_id = 1;
} else if(fcf->src_addr_mode) {
/* Only src address, include src PAN ID */
src_pan_id = 1;
}
} else if((fcf->dest_addr_mode == 0) && (fcf->src_addr_mode == 0)) {
/* No address included: PAN ID compression flag changes meaning */
/*
* IEEE 802.15.4-2015
* Table 7-2, PAN ID Compression value for frame version 0b10
*/
if((fcf->dest_addr_mode == FRAME802154_NOADDR &&
fcf->src_addr_mode == FRAME802154_NOADDR &&
fcf->panid_compression == 1) ||
(fcf->dest_addr_mode != FRAME802154_NOADDR &&
fcf->src_addr_mode == FRAME802154_NOADDR &&
fcf->panid_compression == 0) ||
(fcf->dest_addr_mode == FRAME802154_LONGADDRMODE &&
fcf->src_addr_mode == FRAME802154_LONGADDRMODE &&
fcf->panid_compression == 0) ||
((fcf->dest_addr_mode == FRAME802154_SHORTADDRMODE &&
fcf->src_addr_mode != FRAME802154_NOADDR) ||
(fcf->dest_addr_mode != FRAME802154_NOADDR &&
fcf->src_addr_mode == FRAME802154_SHORTADDRMODE)) ){
dest_pan_id = 1;
}
if(fcf->panid_compression == 0 &&
((fcf->dest_addr_mode == FRAME802154_NOADDR &&
fcf->src_addr_mode == FRAME802154_LONGADDRMODE) ||
(fcf->dest_addr_mode == FRAME802154_NOADDR &&
fcf->src_addr_mode == FRAME802154_SHORTADDRMODE) ||
(fcf->dest_addr_mode == FRAME802154_SHORTADDRMODE &&
fcf->src_addr_mode == FRAME802154_SHORTADDRMODE) ||
(fcf->dest_addr_mode == FRAME802154_SHORTADDRMODE &&
fcf->src_addr_mode == FRAME802154_LONGADDRMODE) ||
(fcf->dest_addr_mode == FRAME802154_LONGADDRMODE &&
fcf->src_addr_mode == FRAME802154_SHORTADDRMODE))) {
src_pan_id = 1;
}
} else {
/* No PAN ID in ACK */
if(fcf->frame_type != FRAME802154_ACKFRAME) {
if(!fcf->panid_compression && fcf->src_addr_mode & 3) {
/* If compressed, don't inclue source PAN ID */
/* If compressed, don't include source PAN ID */
src_pan_id = 1;
}
if(fcf->dest_addr_mode & 3) {

View file

@ -70,7 +70,7 @@ struct ieee802154_ies {
uint16_t ie_mlme_len;
/* Payload Short MLME IEs */
uint8_t ie_tsch_synchronization_offset;
struct asn_t ie_asn;
struct tsch_asn_t ie_asn;
uint8_t ie_join_priority;
uint8_t ie_tsch_timeslot_id;
uint16_t ie_tsch_timeslot[tsch_ts_elements_count];

View file

@ -173,7 +173,8 @@ create_frame(int type, int do_create)
* Set up the source address using only the long address mode for
* phase 1.
*/
linkaddr_copy((linkaddr_t *)&params.src_addr, &linkaddr_node_addr);
linkaddr_copy((linkaddr_t *)&params.src_addr,
packetbuf_addr(PACKETBUF_ADDR_SENDER));
params.payload = packetbuf_dataptr();
params.payload_len = packetbuf_datalen();

View file

@ -46,10 +46,10 @@
#include "net/rime/rimestats.h"
#include <string.h>
#if CONTIKI_TARGET_COOJA
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
#include "lib/simEnvChange.h"
#include "sys/cooja_mt.h"
#endif /* CONTIKI_TARGET_COOJA */
#endif /* CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64 */
#define DEBUG 0
#if DEBUG
@ -158,10 +158,10 @@ send_one_packet(mac_callback_t sent, void *ptr)
wt = RTIMER_NOW();
watchdog_periodic();
while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + ACK_WAIT_TIME)) {
#if CONTIKI_TARGET_COOJA
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
simProcessRunValue = 1;
cooja_mt_yield();
#endif /* CONTIKI_TARGET_COOJA */
#endif /* CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64 */
}
ret = MAC_TX_NOACK;
@ -176,10 +176,10 @@ send_one_packet(mac_callback_t sent, void *ptr)
watchdog_periodic();
while(RTIMER_CLOCK_LT(RTIMER_NOW(),
wt + AFTER_ACK_DETECTED_WAIT_TIME)) {
#if CONTIKI_TARGET_COOJA
simProcessRunValue = 1;
cooja_mt_yield();
#endif /* CONTIKI_TARGET_COOJA */
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
simProcessRunValue = 1;
cooja_mt_yield();
#endif /* CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64 */
}
}
@ -195,8 +195,8 @@ send_one_packet(mac_callback_t sent, void *ptr)
}
}
} else {
PRINTF("nullrdc tx noack\n");
}
PRINTF("nullrdc tx noack\n");
}
}
break;
case RADIO_TX_COLLISION:
@ -303,7 +303,7 @@ packet_input(void)
}
#endif /* RDC_WITH_DUPLICATE_DETECTION */
#endif /* NULLRDC_802154_AUTOACK */
#if NULLRDC_SEND_802154_ACK
{
frame802154_t info154;

View file

@ -42,6 +42,7 @@ It has been tested on the following platforms:
* Zolertia Zoul (`zoul`, tested on hardware)
* OpenMote-CC2538 (`openmote-cc2538`, tested on hardware)
* CC2650 (`srf06-cc26xx`, tested on hardware)
* Cooja mote (`cooja`, tested with cooja)
This implementation was present at the ETSI Plugtest
event in Prague in July 2015, and did successfully inter-operate with all
@ -83,7 +84,7 @@ Orchestra is implemented in:
A simple TSCH+RPL example is included under `examples/ipv6/rpl-tsch`.
To use TSCH, first make sure your platform supports it.
Currently, `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul`, `openmote-cc2538`, and `srf06-cc26xx` are the supported platforms.
Currently, `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul`, `openmote-cc2538`, `srf06-cc26xx`, and `cooja` are the supported platforms.
To add your own, we refer the reader to the next section.
To add TSCH to your application, first include the TSCH module from your makefile with:
@ -179,7 +180,7 @@ Instead, TSCH will poll the driver for incoming packets, from interrupt, exactly
TSCH will check when initializing (in `tsch_init`) that the radio driver supports all required features, namely:
* get and set Rx mode (`RADIO_PARAM_RX_MODE`) as follows:
* enable address filtering with `RADIO_RX_MODE_ADDRESS_FILTER`
* disable address filtering with `RADIO_RX_MODE_ADDRESS_FILTER`
* disable auto-ack with `RADIO_RX_MODE_AUTOACK`
* enable poll mode with `RADIO_RX_MODE_POLL_MODE`
* get and set Tx mode (`RADIO_PARAM_TX_MODE`) as follows:

View file

@ -38,8 +38,10 @@
*
*/
#include "tsch-adaptive-timesync.h"
#include "tsch-log.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-conf.h"
#include "net/mac/tsch/tsch-adaptive-timesync.h"
#include "net/mac/tsch/tsch-log.h"
#include <stdio.h>
#if TSCH_ADAPTIVE_TIMESYNC
@ -72,6 +74,10 @@ timesync_entry_add(int32_t val, uint32_t time_delta)
buffer[pos] = val;
if(timesync_entry_count < NUM_TIMESYNC_ENTRIES) {
timesync_entry_count++;
} else {
/* We now have accurate drift compensation.
* Increase keep-alive timeout. */
tsch_set_ka_timeout(TSCH_MAX_KEEPALIVE_TIMEOUT);
}
pos = (pos + 1) % NUM_TIMESYNC_ENTRIES;

View file

@ -44,13 +44,13 @@
/************ Types ***********/
/* The ASN is an absolute slot number over 5 bytes. */
struct asn_t {
struct tsch_asn_t {
uint32_t ls4b; /* least significant 4 bytes */
uint8_t ms1b; /* most significant 1 byte */
};
/* For quick modulo operation on ASN */
struct asn_divisor_t {
struct tsch_asn_divisor_t {
uint16_t val; /* Divisor value */
uint16_t asn_ms1b_remainder; /* Remainder of the operation 0x100000000 / val */
};
@ -58,38 +58,38 @@ struct asn_divisor_t {
/************ Macros **********/
/* Initialize ASN */
#define ASN_INIT(asn, ms1b_, ls4b_) do { \
#define TSCH_ASN_INIT(asn, ms1b_, ls4b_) do { \
(asn).ms1b = (ms1b_); \
(asn).ls4b = (ls4b_); \
} while(0);
/* Increment an ASN by inc (32 bits) */
#define ASN_INC(asn, inc) do { \
#define TSCH_ASN_INC(asn, inc) do { \
uint32_t new_ls4b = (asn).ls4b + (inc); \
if(new_ls4b < (asn).ls4b) { (asn).ms1b++; } \
(asn).ls4b = new_ls4b; \
} while(0);
/* Decrement an ASN by inc (32 bits) */
#define ASN_DEC(asn, dec) do { \
#define TSCH_ASN_DEC(asn, dec) do { \
uint32_t new_ls4b = (asn).ls4b - (dec); \
if(new_ls4b > (asn).ls4b) { (asn).ms1b--; } \
(asn).ls4b = new_ls4b; \
} while(0);
/* Returns the 32-bit diff between asn1 and asn2 */
#define ASN_DIFF(asn1, asn2) \
#define TSCH_ASN_DIFF(asn1, asn2) \
((asn1).ls4b - (asn2).ls4b)
/* Initialize a struct asn_divisor_t */
#define ASN_DIVISOR_INIT(div, val_) do { \
#define TSCH_ASN_DIVISOR_INIT(div, val_) do { \
(div).val = (val_); \
(div).asn_ms1b_remainder = ((0xffffffff % (val_)) + 1) % (val_); \
} while(0);
/* Returns the result (16 bits) of a modulo operation on ASN,
* with divisor being a struct asn_divisor_t */
#define ASN_MOD(asn, div) \
#define TSCH_ASN_MOD(asn, div) \
((uint16_t)((asn).ls4b % (div).val) \
+ (uint16_t)((asn).ms1b * (div).asn_ms1b_remainder % (div).val)) \
% (div).val

View file

@ -174,7 +174,7 @@
#ifdef TSCH_CONF_ADAPTIVE_TIMESYNC
#define TSCH_ADAPTIVE_TIMESYNC TSCH_CONF_ADAPTIVE_TIMESYNC
#else
#define TSCH_ADAPTIVE_TIMESYNC 0
#define TSCH_ADAPTIVE_TIMESYNC 1
#endif
/* HW frame filtering enabled */

View file

@ -132,7 +132,7 @@ tsch_log_prepare_add(void)
int log_index = ringbufindex_peek_put(&log_ringbuf);
if(log_index != -1) {
struct tsch_log_t *log = &log_array[log_index];
log->asn = current_asn;
log->asn = tsch_current_asn;
log->link = current_link;
return log;
} else {

View file

@ -81,7 +81,7 @@ struct tsch_log_t {
tsch_log_rx,
tsch_log_message
} type;
struct asn_t asn;
struct tsch_asn_t asn;
struct tsch_link *link;
union {
char message[48];

View file

@ -66,7 +66,7 @@
/* Construct enhanced ACK packet and return ACK length */
int
tsch_packet_create_eack(uint8_t *buf, int buf_size,
linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack)
const linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack)
{
int ret;
uint8_t curr_len = 0;
@ -125,7 +125,7 @@ tsch_packet_create_eack(uint8_t *buf, int buf_size,
/* Parse enhanced ACK packet, extract drift and nack */
int
tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
{
uint8_t curr_len = 0;
int ret;
@ -155,8 +155,8 @@ tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
/* Check destination address (if any) */
if(frame802154_extract_linkaddr(frame, NULL, &dest) == 0 ||
(!linkaddr_cmp(&dest, &linkaddr_node_addr)
&& !linkaddr_cmp(&dest, &linkaddr_null))) {
(!linkaddr_cmp(&dest, &linkaddr_node_addr)
&& !linkaddr_cmp(&dest, &linkaddr_null))) {
return 0;
}
@ -190,7 +190,7 @@ tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
/* Create an EB packet */
int
tsch_packet_create_eb(uint8_t *buf, int buf_size,
uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
{
int ret = 0;
uint8_t curr_len = 0;
@ -338,7 +338,7 @@ int
tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
{
struct ieee802154_ies ies;
ies.ie_asn = current_asn;
ies.ie_asn = tsch_current_asn;
ies.ie_join_priority = tsch_join_priority;
frame80215e_create_ie_tsch_synchronization(buf+tsch_sync_ie_offset, buf_size-tsch_sync_ie_offset, &ies);
return 1;
@ -347,7 +347,7 @@ tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
/* Parse a IEEE 802.15.4e TSCH Enhanced Beacon (EB) */
int
tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
{
uint8_t curr_len = 0;
int ret;

View file

@ -88,7 +88,7 @@ by default, useful in case of duplicate seqno */
/* Construct enhanced ACK packet and return ACK length */
int tsch_packet_create_eack(uint8_t *buf, int buf_size,
linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack);
const linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack);
/* Parse enhanced ACK packet, extract drift and nack */
int tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len);

View file

@ -48,6 +48,10 @@
#include "net/linkaddr.h"
#include "net/mac/tsch/tsch-asn.h"
#include "net/mac/tsch/tsch-conf.h"
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
#include "lib/simEnvChange.h"
#include "sys/cooja_mt.h"
#endif /* CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64 */
/************ Types ***********/
@ -76,12 +80,12 @@ extern const linkaddr_t tsch_broadcast_address;
/* The address we use to identify EB queue */
extern const linkaddr_t tsch_eb_address;
/* The current Absolute Slot Number (ASN) */
extern struct asn_t current_asn;
extern struct tsch_asn_t tsch_current_asn;
extern uint8_t tsch_join_priority;
extern struct tsch_link *current_link;
/* TSCH channel hopping sequence */
extern uint8_t tsch_hopping_sequence[TSCH_HOPPING_SEQUENCE_MAX_LEN];
extern struct asn_divisor_t tsch_hopping_sequence_length;
extern struct tsch_asn_divisor_t tsch_hopping_sequence_length;
/* TSCH timeslot timing (in rtimer ticks) */
extern rtimer_clock_t tsch_timing[tsch_ts_elements_count];
@ -109,7 +113,14 @@ void tsch_disassociate(void);
#define TSCH_CLOCK_TO_SLOTS(c, timeslot_length) (TSCH_CLOCK_TO_TICKS(c) / timeslot_length)
/* Wait for a condition with timeout t0+offset. */
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
#define BUSYWAIT_UNTIL_ABS(cond, t0, offset) \
while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), (t0) + (offset))) { \
simProcessRunValue = 1; \
cooja_mt_yield(); \
};
#else
#define BUSYWAIT_UNTIL_ABS(cond, t0, offset) \
while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), (t0) + (offset))) ;
#endif /* CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64 */
#endif /* __TSCH_PRIVATE_H__ */

View file

@ -163,6 +163,13 @@ tsch_queue_update_time_source(const linkaddr_t *new_addr)
/* Update time source */
if(new_time_src != NULL) {
new_time_src->is_time_source = 1;
/* (Re)set keep-alive timeout */
tsch_set_ka_timeout(TSCH_KEEPALIVE_TIMEOUT);
/* Start sending keepalives */
tsch_schedule_keepalive();
} else {
/* Stop sending keepalives */
tsch_set_ka_timeout(0);
}
if(old_time_src != NULL) {

View file

@ -85,7 +85,7 @@ tsch_rpl_callback_new_dio_interval(uint8_t dio_interval)
tsch_set_coordinator(1);
}
/* Set EB period */
tsch_set_eb_period(TSCH_EB_PERIOD);
tsch_set_eb_period((CLOCK_SECOND * 1UL << dag->instance->dio_intcurrent) / 1000);
/* Set join priority based on RPL rank */
tsch_set_join_priority(DAG_RANK(dag->rank, dag->instance) - 1);
} else {

View file

@ -87,7 +87,7 @@ tsch_schedule_add_slotframe(uint16_t handle, uint16_t size)
if(sf != NULL) {
/* Initialize the slotframe */
sf->handle = handle;
ASN_DIVISOR_INIT(sf->size, size);
TSCH_ASN_DIVISOR_INIT(sf->size, size);
LIST_STRUCT_INIT(sf, links_list);
/* Add the slotframe to the global list */
list_add(slotframe_list, sf);
@ -310,7 +310,7 @@ tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t ti
/*---------------------------------------------------------------------------*/
/* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
struct tsch_link *
tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
tsch_schedule_get_next_active_link(struct tsch_asn_t *asn, uint16_t *time_offset,
struct tsch_link **backup_link)
{
uint16_t time_to_curr_best = 0;
@ -324,7 +324,7 @@ tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
/* For each slotframe, look for the earliest occurring link */
while(sf != NULL) {
/* Get timeslot from ASN, given the slotframe length */
uint16_t timeslot = ASN_MOD(*asn, sf->size);
uint16_t timeslot = TSCH_ASN_MOD(*asn, sf->size);
struct tsch_link *l = list_head(sf->links_list);
while(l != NULL) {
uint16_t time_to_timeslot =

View file

@ -119,7 +119,7 @@ struct tsch_slotframe {
uint16_t handle;
/* Number of timeslots in the slotframe.
* Stored as struct asn_divisor_t because we often need ASN%size */
struct asn_divisor_t size;
struct tsch_asn_divisor_t size;
/* List of links belonging to this slotframe */
LIST_STRUCT(links_list);
};
@ -158,7 +158,7 @@ int tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link
int tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot);
/* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
struct tsch_link * tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
struct tsch_link * tsch_schedule_get_next_active_link(struct tsch_asn_t *asn, uint16_t *time_offset,
struct tsch_link **backup_link);
#endif /* __TSCH_SCHEDULE_H__ */

View file

@ -73,7 +73,7 @@ static aes_key keys[] = {
/*---------------------------------------------------------------------------*/
static void
tsch_security_init_nonce(uint8_t *nonce,
const linkaddr_t *sender, struct asn_t *asn)
const linkaddr_t *sender, struct tsch_asn_t *asn)
{
memcpy(nonce, sender, 8);
nonce[8] = asn->ms1b;
@ -120,11 +120,12 @@ tsch_security_check_level(const frame802154_t *frame)
required_key_index = TSCH_SECURITY_KEY_INDEX_OTHER;
break;
}
return frame->aux_hdr.security_control.security_level == required_security_level
&& frame->aux_hdr.key_index == required_key_index;
return ((frame->aux_hdr.security_control.security_level ==
required_security_level) &&
frame->aux_hdr.key_index == required_key_index);
}
/*---------------------------------------------------------------------------*/
int
unsigned int
tsch_security_mic_len(const frame802154_t *frame)
{
if(frame != NULL && frame->fcf.security_enabled) {
@ -134,9 +135,9 @@ tsch_security_mic_len(const frame802154_t *frame)
}
}
/*---------------------------------------------------------------------------*/
int
unsigned int
tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
int hdrlen, int datalen, struct asn_t *asn)
int hdrlen, int datalen, struct tsch_asn_t *asn)
{
frame802154_t frame;
uint8_t key_index = 0;
@ -159,7 +160,7 @@ tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
if(!frame.fcf.security_enabled) {
/* Security is not enabled for this frame, we're done */
return 1;
return 0;
}
/* Read security key index */
@ -190,17 +191,17 @@ tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
CCM_STAR.set_key(keys[key_index - 1]);
CCM_STAR.aead(nonce,
outbuf + a_len, m_len,
outbuf, a_len,
outbuf + hdrlen + datalen, mic_len, 1
);
outbuf + a_len, m_len,
outbuf, a_len,
outbuf + hdrlen + datalen, mic_len, 1);
return mic_len;
}
/*---------------------------------------------------------------------------*/
int
unsigned int
tsch_security_parse_frame(const uint8_t *hdr, int hdrlen, int datalen,
const frame802154_t *frame, const linkaddr_t *sender, struct asn_t *asn)
const frame802154_t *frame, const linkaddr_t *sender,
struct tsch_asn_t *asn)
{
uint8_t generated_mic[16];
uint8_t key_index = 0;
@ -248,10 +249,9 @@ tsch_security_parse_frame(const uint8_t *hdr, int hdrlen, int datalen,
CCM_STAR.set_key(keys[key_index - 1]);
CCM_STAR.aead(nonce,
(uint8_t *)hdr + a_len, m_len,
(uint8_t *)hdr, a_len,
generated_mic, mic_len, 0
);
(uint8_t *)hdr + a_len, m_len,
(uint8_t *)hdr, a_len,
generated_mic, mic_len, 0);
if(mic_len > 0 && memcmp(generated_mic, hdr + hdrlen + datalen, mic_len) != 0) {
return 0;

View file

@ -118,11 +118,28 @@
typedef uint8_t aes_key[16];
/********** Functions *********/
/**
* \brief Return MIC length
* \return The length of MIC (>= 0)
*/
unsigned int tsch_security_mic_len(const frame802154_t *frame);
int tsch_security_mic_len(const frame802154_t *frame);
int tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
int hdrlen, int datalen, struct asn_t *asn);
int tsch_security_parse_frame(const uint8_t *hdr, int hdrlen, int datalen,
const frame802154_t *frame, const linkaddr_t *sender, struct asn_t *asn);
/**
* \brief Protect a frame with encryption and/or MIC
* \return The length of a generated MIC (>= 0)
*/
unsigned int tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
int hdrlen, int datalen,
struct tsch_asn_t *asn);
/**
* \brief Parse and check a frame protected with encryption and/or MIC
* \retval 0 On error or security check failure (insecure frame)
* \retval 1 On success or no need for security check (good frame)
*/
unsigned int tsch_security_parse_frame(const uint8_t *hdr, int hdrlen,
int datalen, const frame802154_t *frame,
const linkaddr_t *sender,
struct tsch_asn_t *asn);
#endif /* __TSCH_SECURITY_H__ */

View file

@ -54,6 +54,10 @@
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-security.h"
#include "net/mac/tsch/tsch-adaptive-timesync.h"
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
#include "lib/simEnvChange.h"
#include "sys/cooja_mt.h"
#endif /* CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64 */
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
@ -104,7 +108,10 @@
#if RTIMER_SECOND < (32 * 1024)
#error "TSCH: RTIMER_SECOND < (32 * 1024)"
#endif
#if RTIMER_SECOND >= 200000
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
/* Use 0 usec guard time for Cooja Mote with a 1 MHz Rtimer*/
#define RTIMER_GUARD 0u
#elif RTIMER_SECOND >= 200000
#define RTIMER_GUARD (RTIMER_SECOND / 100000)
#else
#define RTIMER_GUARD 2u
@ -132,7 +139,7 @@ struct ringbufindex input_ringbuf;
struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS];
/* Last time we received Sync-IE (ACK or data packet from a time source) */
static struct asn_t last_sync_asn;
static struct tsch_asn_t last_sync_asn;
/* A global lock for manipulating data structures safely from outside of interrupt */
static volatile int tsch_locked = 0;
@ -200,7 +207,12 @@ tsch_get_lock(void)
if(tsch_in_slot_operation) {
busy_wait = 1;
busy_wait_time = RTIMER_NOW();
while(tsch_in_slot_operation);
while(tsch_in_slot_operation) {
#if CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64
simProcessRunValue = 1;
cooja_mt_yield();
#endif /* CONTIKI_TARGET_COOJA || CONTIKI_TARGET_COOJA_IP64 */
}
busy_wait_time = RTIMER_NOW() - busy_wait_time;
}
if(!tsch_locked) {
@ -236,9 +248,9 @@ tsch_release_lock(void)
/* Return channel from ASN and channel offset */
uint8_t
tsch_calculate_channel(struct asn_t *asn, uint8_t channel_offset)
tsch_calculate_channel(struct tsch_asn_t *asn, uint8_t channel_offset)
{
uint16_t index_of_0 = ASN_MOD(*asn, tsch_hopping_sequence_length);
uint16_t index_of_0 = TSCH_ASN_MOD(*asn, tsch_hopping_sequence_length);
uint16_t index_of_offset = (index_of_0 + channel_offset) % tsch_hopping_sequence_length.val;
return tsch_hopping_sequence[index_of_offset];
}
@ -518,7 +530,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
* the original untouched. This is to allow for future retransmissions. */
int with_encryption = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL) & 0x4;
packet_len += tsch_security_secure_frame(packet, with_encryption ? encrypted_packet : packet, current_packet->header_len,
packet_len - current_packet->header_len, &current_asn);
packet_len - current_packet->header_len, &tsch_current_asn);
if(with_encryption) {
packet = encrypted_packet;
}
@ -615,7 +627,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
#if LLSEC802154_ENABLED
if(ack_len != 0) {
if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
&frame, &current_neighbor->addr, &current_asn)) {
&frame, &current_neighbor->addr, &tsch_current_asn)) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to authenticate ACK"));
@ -632,7 +644,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
if(ack_len != 0) {
if(is_time_source) {
int32_t eack_time_correction = US_TO_RTIMERTICKS(ack_ies.ie_time_correction);
int32_t since_last_timesync = ASN_DIFF(current_asn, last_sync_asn);
int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn);
if(eack_time_correction > SYNC_IE_BOUND) {
drift_correction = SYNC_IE_BOUND;
} else if(eack_time_correction < -SYNC_IE_BOUND) {
@ -649,7 +661,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
is_drift_correction_used = 1;
tsch_timesync_update(current_neighbor, since_last_timesync, drift_correction);
/* Keep track of sync time */
last_sync_asn = current_asn;
last_sync_asn = tsch_current_asn;
tsch_schedule_keepalive();
}
mac_tx_status = MAC_TX_OK;
@ -781,7 +793,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
/* Read packet */
current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN);
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
current_input->rx_asn = current_asn;
current_input->rx_asn = tsch_current_asn;
current_input->rssi = (signed)radio_last_rssi;
current_input->channel = current_channel;
header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame);
@ -801,7 +813,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
if(frame_valid) {
if(tsch_security_parse_frame(
current_input->payload, header_len, current_input->len - header_len - tsch_security_mic_len(&frame),
&frame, &source_address, &current_asn)) {
&frame, &source_address, &tsch_current_asn)) {
current_input->len -= tsch_security_mic_len(&frame);
} else {
TSCH_LOG_ADD(tsch_log_message,
@ -849,30 +861,32 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
&source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
if(ack_len > 0) {
#if LLSEC802154_ENABLED
if(tsch_is_pan_secured) {
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, &current_asn);
}
if(tsch_is_pan_secured) {
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, &tsch_current_asn);
}
#endif /* LLSEC802154_ENABLED */
/* Copy to radio buffer */
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
/* Copy to radio buffer */
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
/* Wait for time to ACK and transmit ACK */
TSCH_SCHEDULE_AND_YIELD(pt, t, rx_start_time,
packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.transmit(ack_len);
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
/* Wait for time to ACK and transmit ACK */
TSCH_SCHEDULE_AND_YIELD(pt, t, rx_start_time,
packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.transmit(ack_len);
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
}
}
/* If the sender is a time source, proceed to clock drift compensation */
n = tsch_queue_get_nbr(&source_address);
if(n != NULL && n->is_time_source) {
int32_t since_last_timesync = ASN_DIFF(current_asn, last_sync_asn);
int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn);
/* Keep track of last sync time */
last_sync_asn = current_asn;
last_sync_asn = tsch_current_asn;
/* Save estimated drift */
drift_correction = -estimated_drift;
is_drift_correction_used = 1;
@ -958,7 +972,7 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX);
if(is_active_slot) {
/* Hop channel */
current_channel = tsch_calculate_channel(&current_asn, current_link->channel_offset);
current_channel = tsch_calculate_channel(&tsch_current_asn, current_link->channel_offset);
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
/* Turn the radio on already here if configured so; necessary for radios with slow startup */
tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
@ -984,12 +998,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
/* End of slot operation, schedule next slot or resynchronize */
/* Do we need to resynchronize? i.e., wait for EB again */
if(!tsch_is_coordinator && (ASN_DIFF(current_asn, last_sync_asn) >
if(!tsch_is_coordinator && (TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn) >
(100 * TSCH_CLOCK_TO_SLOTS(TSCH_DESYNC_THRESHOLD / 100, tsch_timing[tsch_ts_timeslot_length])))) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"! leaving the network, last sync %u",
(unsigned)ASN_DIFF(current_asn, last_sync_asn));
(unsigned)TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn));
);
last_timesource_neighbor = NULL;
tsch_disassociate();
@ -1011,14 +1025,14 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
}
/* Get next active link */
current_link = tsch_schedule_get_next_active_link(&current_asn, &timeslot_diff, &backup_link);
current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, &timeslot_diff, &backup_link);
if(current_link == NULL) {
/* There is no next link. Fall back to default
* behavior: wake up at the next slot. */
timeslot_diff = 1;
}
/* Update ASN */
ASN_INC(current_asn, timeslot_diff);
TSCH_ASN_INC(tsch_current_asn, timeslot_diff);
/* Time to next wake up */
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length] + drift_correction;
drift_correction = 0;
@ -1049,14 +1063,14 @@ tsch_slot_operation_start(void)
do {
uint16_t timeslot_diff;
/* Get next active link */
current_link = tsch_schedule_get_next_active_link(&current_asn, &timeslot_diff, &backup_link);
current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, &timeslot_diff, &backup_link);
if(current_link == NULL) {
/* There is no next link. Fall back to default
* behavior: wake up at the next slot. */
timeslot_diff = 1;
}
/* Update ASN */
ASN_INC(current_asn, timeslot_diff);
TSCH_ASN_INC(tsch_current_asn, timeslot_diff);
/* Time to next wake up */
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length];
/* Update current slot start */
@ -1068,11 +1082,11 @@ tsch_slot_operation_start(void)
/* Start actual slot operation */
void
tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
struct asn_t *next_slot_asn)
struct tsch_asn_t *next_slot_asn)
{
current_slot_start = next_slot_start;
current_asn = *next_slot_asn;
last_sync_asn = current_asn;
tsch_current_asn = *next_slot_asn;
last_sync_asn = tsch_current_asn;
current_link = NULL;
}
/*---------------------------------------------------------------------------*/

View file

@ -87,7 +87,7 @@ int TSCH_CALLBACK_DO_NACK(struct tsch_link *link, linkaddr_t *src, linkaddr_t *d
/* Stores data about an incoming packet */
struct input_packet {
uint8_t payload[TSCH_PACKET_MAX_LEN]; /* Packet payload */
struct asn_t rx_asn; /* ASN when the packet was received */
struct tsch_asn_t rx_asn; /* ASN when the packet was received */
int len; /* Packet len */
int16_t rssi; /* RSSI for this packet */
uint8_t channel; /* Channel we received the packet on */
@ -107,7 +107,7 @@ extern struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS];
/********** Functions *********/
/* Returns a 802.15.4 channel from an ASN and channel offset */
uint8_t tsch_calculate_channel(struct asn_t *asn, uint8_t channel_offset);
uint8_t tsch_calculate_channel(struct tsch_asn_t *asn, uint8_t channel_offset);
/* Is TSCH locked? */
int tsch_is_locked(void);
/* Lock TSCH (no link operation) */
@ -117,7 +117,7 @@ void tsch_release_lock(void);
/* Set global time before starting slot operation,
* with a rtimer time and an ASN */
void tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
struct asn_t *next_slot_asn);
struct tsch_asn_t *next_slot_asn);
/* Start actual slot operation */
void tsch_slot_operation_start(void);

View file

@ -90,7 +90,7 @@ NBR_TABLE(struct eb_stat, eb_stats);
/* TSCH channel hopping sequence */
uint8_t tsch_hopping_sequence[TSCH_HOPPING_SEQUENCE_MAX_LEN];
struct asn_divisor_t tsch_hopping_sequence_length;
struct tsch_asn_divisor_t tsch_hopping_sequence_length;
/* Default TSCH timeslot timing (in micro-second) */
static const uint16_t tsch_default_timing_us[tsch_ts_elements_count] = {
@ -131,7 +131,7 @@ int tsch_is_associated = 0;
/* Is the PAN running link-layer security? */
int tsch_is_pan_secured = LLSEC802154_ENABLED;
/* The current Absolute Slot Number (ASN) */
struct asn_t current_asn;
struct tsch_asn_t tsch_current_asn;
/* Device rank or join priority:
* For PAN coordinator: 0 -- lower is better */
uint8_t tsch_join_priority;
@ -139,6 +139,8 @@ uint8_t tsch_join_priority;
static uint8_t tsch_packet_seqno = 0;
/* Current period for EB output */
static clock_time_t tsch_current_eb_period;
/* Current period for keepalive output */
static clock_time_t tsch_current_ka_timeout;
/* timer for sending keepalive messages */
static struct ctimer keepalive_timer;
@ -175,9 +177,15 @@ tsch_set_join_priority(uint8_t jp)
}
/*---------------------------------------------------------------------------*/
void
tsch_set_ka_timeout(uint32_t timeout)
{
tsch_current_ka_timeout = timeout;
}
/*---------------------------------------------------------------------------*/
void
tsch_set_eb_period(uint32_t period)
{
tsch_current_eb_period = period;
tsch_current_eb_period = MIN(period, TSCH_MAX_EB_PERIOD);
}
/*---------------------------------------------------------------------------*/
static void
@ -194,7 +202,7 @@ tsch_reset(void)
tsch_queue_update_time_source(NULL);
/* Initialize global variables */
tsch_join_priority = 0xff;
ASN_INIT(current_asn, 0, 0);
TSCH_ASN_INIT(tsch_current_asn, 0, 0);
current_link = NULL;
/* Reset timeslot timing to defaults */
for(i = 0; i < tsch_ts_elements_count; i++) {
@ -244,10 +252,10 @@ keepalive_send()
void
tsch_schedule_keepalive()
{
/* Pick a delay in the range [TSCH_KEEPALIVE_TIMEOUT*0.9, TSCH_KEEPALIVE_TIMEOUT[ */
if(!tsch_is_coordinator && tsch_is_associated) {
unsigned long delay = (TSCH_KEEPALIVE_TIMEOUT - TSCH_KEEPALIVE_TIMEOUT / 10)
+ random_rand() % (TSCH_KEEPALIVE_TIMEOUT / 10);
/* Pick a delay in the range [tsch_current_ka_timeout*0.9, tsch_current_ka_timeout[ */
if(!tsch_is_coordinator && tsch_is_associated && tsch_current_ka_timeout > 0) {
unsigned long delay = (tsch_current_ka_timeout - tsch_current_ka_timeout / 10)
+ random_rand() % (tsch_current_ka_timeout / 10);
ctimer_set(&keepalive_timer, delay, keepalive_send, NULL);
}
}
@ -302,7 +310,7 @@ eb_input(struct input_packet *current_input)
/* Did the EB come from our time source? */
if(n != NULL && linkaddr_cmp((linkaddr_t *)&frame.src_addr, &n->addr)) {
/* Check for ASN drift */
int32_t asn_diff = ASN_DIFF(current_input->rx_asn, eb_ies.ie_asn);
int32_t asn_diff = TSCH_ASN_DIFF(current_input->rx_asn, eb_ies.ie_asn);
if(asn_diff != 0) {
/* We disagree with our time source's ASN -- leave the network */
PRINTF("TSCH:! ASN drifted by %ld, leaving the network\n", asn_diff);
@ -393,7 +401,7 @@ tsch_start_coordinator(void)
frame802154_set_pan_id(IEEE802154_PANID);
/* Initialize hopping sequence as default */
memcpy(tsch_hopping_sequence, TSCH_DEFAULT_HOPPING_SEQUENCE, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
#if TSCH_SCHEDULE_WITH_6TISCH_MINIMAL
tsch_schedule_create_minimal();
#endif
@ -402,10 +410,10 @@ tsch_start_coordinator(void)
tsch_join_priority = 0;
PRINTF("TSCH: starting as coordinator, PAN ID %x, asn-%x.%lx\n",
frame802154_get_pan_id(), current_asn.ms1b, current_asn.ls4b);
frame802154_get_pan_id(), tsch_current_asn.ms1b, tsch_current_asn.ls4b);
/* Start slot operation */
tsch_slot_operation_sync(RTIMER_NOW(), &current_asn);
tsch_slot_operation_sync(RTIMER_NOW(), &tsch_current_asn);
}
/*---------------------------------------------------------------------------*/
/* Leave the TSCH network */
@ -434,7 +442,7 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
return 0;
}
current_asn = ies.ie_asn;
tsch_current_asn = ies.ie_asn;
tsch_join_priority = ies.ie_join_priority + 1;
#if TSCH_JOIN_SECURED_ONLY
@ -447,7 +455,7 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
#if LLSEC802154_ENABLED
if(!tsch_security_parse_frame(input_eb->payload, hdrlen,
input_eb->len - hdrlen - tsch_security_mic_len(&frame),
&frame, (linkaddr_t*)&frame.src_addr, &current_asn)) {
&frame, (linkaddr_t*)&frame.src_addr, &tsch_current_asn)) {
PRINTF("TSCH:! parse_eb: failed to authenticate\n");
return 0;
}
@ -486,11 +494,11 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
/* TSCH hopping sequence */
if(ies.ie_channel_hopping_sequence_id == 0) {
memcpy(tsch_hopping_sequence, TSCH_DEFAULT_HOPPING_SEQUENCE, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
} else {
if(ies.ie_hopping_sequence_len <= sizeof(tsch_hopping_sequence)) {
memcpy(tsch_hopping_sequence, ies.ie_hopping_sequence_list, ies.ie_hopping_sequence_len);
ASN_DIVISOR_INIT(tsch_hopping_sequence_length, ies.ie_hopping_sequence_len);
TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, ies.ie_hopping_sequence_len);
} else {
PRINTF("TSCH:! parse_eb: hopping sequence too long (%u)\n", ies.ie_hopping_sequence_len);
return 0;
@ -501,10 +509,10 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
/* Divide by 4k and multiply again to avoid integer overflow */
uint32_t expected_asn = 4096 * TSCH_CLOCK_TO_SLOTS(clock_time() / 4096, tsch_timing_timeslot_length); /* Expected ASN based on our current time*/
int32_t asn_threshold = TSCH_CHECK_TIME_AT_ASSOCIATION * 60ul * TSCH_CLOCK_TO_SLOTS(CLOCK_SECOND, tsch_timing_timeslot_length);
int32_t asn_diff = (int32_t)current_asn.ls4b - expected_asn;
int32_t asn_diff = (int32_t)tsch_current_asn.ls4b - expected_asn;
if(asn_diff > asn_threshold) {
PRINTF("TSCH:! EB ASN rejected %lx %lx %ld\n",
current_asn.ls4b, expected_asn, asn_diff);
tsch_current_asn.ls4b, expected_asn, asn_diff);
return 0;
}
#endif
@ -554,7 +562,7 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
frame802154_set_pan_id(frame.src_pid);
/* Synchronize on EB */
tsch_slot_operation_sync(timestamp - tsch_timing[tsch_ts_tx_offset], &current_asn);
tsch_slot_operation_sync(timestamp - tsch_timing[tsch_ts_tx_offset], &tsch_current_asn);
/* Update global flags */
tsch_is_associated = 1;
@ -570,7 +578,7 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
PRINTF("TSCH: association done, sec %u, PAN ID %x, asn-%x.%lx, jp %u, timeslot id %u, hopping id %u, slotframe len %u with %u links, from ",
tsch_is_pan_secured,
frame.src_pid,
current_asn.ms1b, current_asn.ls4b, tsch_join_priority,
tsch_current_asn.ms1b, tsch_current_asn.ls4b, tsch_join_priority,
ies.ie_tsch_timeslot_id,
ies.ie_channel_hopping_sequence_id,
ies.ie_tsch_slotframe_and_link.slotframe_size,
@ -601,7 +609,7 @@ PT_THREAD(tsch_scan(struct pt *pt))
/* Time when we started scanning on current_channel */
static clock_time_t current_channel_since;
ASN_INIT(current_asn, 0, 0);
TSCH_ASN_INIT(tsch_current_asn, 0, 0);
etimer_set(&scan_timer, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY);
current_channel_since = clock_time();
@ -741,7 +749,7 @@ PROCESS_THREAD(tsch_send_eb_process, ev, data)
#endif /* LLSEC802154_ENABLED */
eb_len = tsch_packet_create_eb(packetbuf_dataptr(), PACKETBUF_SIZE,
&hdr_len, &tsch_sync_ie_offset);
if(eb_len != 0) {
if(eb_len > 0) {
struct tsch_packet *p;
packetbuf_set_datalen(eb_len);
/* Enqueue EB packet */
@ -902,6 +910,15 @@ send_packet(mac_callback_t sent, void *ptr)
packet_count_before = tsch_queue_packet_count(addr);
#if !NETSTACK_CONF_BRIDGE_MODE
/*
* In the Contiki stack, the source address of a frame is set at the RDC
* layer. Since TSCH doesn't use any RDC protocol and bypasses the layer to
* transmit a frame, it should set the source address by itself.
*/
packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
#endif
if((hdr_len = NETSTACK_FRAMER.create()) < 0) {
PRINTF("TSCH:! can't send packet due to framer error\n");
ret = MAC_TX_ERR;

View file

@ -49,18 +49,33 @@
#define TSCH_KEEPALIVE_TIMEOUT (12 * CLOCK_SECOND)
#endif
/* With TSCH_ADAPTIVE_TIMESYNC enabled: keep-alive timeout used after reaching
* accurate drift compensation. */
#ifdef TSCH_CONF_MAX_KEEPALIVE_TIMEOUT
#define TSCH_MAX_KEEPALIVE_TIMEOUT TSCH_CONF_MAX_KEEPALIVE_TIMEOUT
#else
#define TSCH_MAX_KEEPALIVE_TIMEOUT (60 * CLOCK_SECOND)
#endif
/* Max time without synchronization before leaving the PAN */
#ifdef TSCH_CONF_DESYNC_THRESHOLD
#define TSCH_DESYNC_THRESHOLD TSCH_CONF_DESYNC_THRESHOLD
#else
#define TSCH_DESYNC_THRESHOLD (4 * TSCH_KEEPALIVE_TIMEOUT)
#define TSCH_DESYNC_THRESHOLD (2 * TSCH_MAX_KEEPALIVE_TIMEOUT)
#endif
/* Period between two consecutive EBs */
#ifdef TSCH_CONF_EB_PERIOD
#define TSCH_EB_PERIOD TSCH_CONF_EB_PERIOD
#else
#define TSCH_EB_PERIOD (4 * CLOCK_SECOND)
#define TSCH_EB_PERIOD (16 * CLOCK_SECOND)
#endif
/* Max Period between two consecutive EBs */
#ifdef TSCH_CONF_MAX_EB_PERIOD
#define TSCH_MAX_EB_PERIOD TSCH_CONF_MAX_EB_PERIOD
#else
#define TSCH_MAX_EB_PERIOD (50 * CLOCK_SECOND)
#endif
/* Max acceptable join priority */
@ -157,8 +172,10 @@ extern const struct mac_driver tschmac_driver;
/* The the TSCH join priority */
void tsch_set_join_priority(uint8_t jp);
/* The the period at which EBs are sent */
/* The period at which EBs are sent */
void tsch_set_eb_period(uint32_t period);
/* The keep-alive timeout */
void tsch_set_ka_timeout(uint32_t timeout);
/* Set the node as PAN coordinator */
void tsch_set_coordinator(int enable);
/* Set the pan as secured or not */

View file

@ -184,6 +184,12 @@ packetbuf_totlen(void)
return packetbuf_hdrlen() + packetbuf_datalen();
}
/*---------------------------------------------------------------------------*/
uint16_t
packetbuf_remaininglen(void)
{
return PACKETBUF_SIZE - packetbuf_totlen();
}
/*---------------------------------------------------------------------------*/
void
packetbuf_attr_clear(void)
{
@ -196,7 +202,7 @@ packetbuf_attr_clear(void)
/*---------------------------------------------------------------------------*/
void
packetbuf_attr_copyto(struct packetbuf_attr *attrs,
struct packetbuf_addr *addrs)
struct packetbuf_addr *addrs)
{
memcpy(attrs, packetbuf_attrs, sizeof(packetbuf_attrs));
memcpy(addrs, packetbuf_addrs, sizeof(packetbuf_addrs));
@ -204,7 +210,7 @@ packetbuf_attr_copyto(struct packetbuf_attr *attrs,
/*---------------------------------------------------------------------------*/
void
packetbuf_attr_copyfrom(struct packetbuf_attr *attrs,
struct packetbuf_addr *addrs)
struct packetbuf_addr *addrs)
{
memcpy(packetbuf_attrs, attrs, sizeof(packetbuf_attrs));
memcpy(packetbuf_addrs, addrs, sizeof(packetbuf_addrs));

View file

@ -123,6 +123,13 @@ uint16_t packetbuf_datalen(void);
*/
uint16_t packetbuf_totlen(void);
/**
* \brief Get the total length of the remaining space in the packetbuf
* \return Length of the remaining space in the packetbuf
*
*/
uint16_t packetbuf_remaininglen(void);
/**
* \brief Set the length of the data in the packetbuf
* \param len The length of the data
@ -241,7 +248,7 @@ enum {
PACKETBUF_ATTR_TSCH_SLOTFRAME,
PACKETBUF_ATTR_TSCH_TIMESLOT,
#endif /* TSCH_WITH_LINK_SELECTOR */
/* Scope 1 attributes: used between two neighbors only. */
#if PACKETBUF_WITH_PACKET_TYPE
PACKETBUF_ATTR_PACKET_TYPE,
@ -267,7 +274,7 @@ enum {
PACKETBUF_ATTR_KEY_INDEX,
PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1,
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
/* Scope 2 attributes: used between end-to-end nodes. */
#if NETSTACK_CONF_WITH_RIME
PACKETBUF_ATTR_HOPS,
@ -343,9 +350,9 @@ int packetbuf_holds_broadcast(void);
void packetbuf_attr_clear(void);
void packetbuf_attr_copyto(struct packetbuf_attr *attrs,
struct packetbuf_addr *addrs);
struct packetbuf_addr *addrs);
void packetbuf_attr_copyfrom(struct packetbuf_attr *attrs,
struct packetbuf_addr *addrs);
struct packetbuf_addr *addrs);
#define PACKETBUF_ATTRIBUTES(...) { __VA_ARGS__ PACKETBUF_ATTR_LAST }
#define PACKETBUF_ATTR_LAST { PACKETBUF_ATTR_NONE, 0 }

View file

@ -215,13 +215,13 @@ rpl_parent_is_reachable(rpl_parent_t *p) {
if(p == NULL || p->dag == NULL || p->dag->instance == NULL || p->dag->instance->of == NULL) {
return 0;
} else {
#ifndef UIP_CONF_ND6_SEND_NA
#if UIP_ND6_SEND_NS
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
/* Exclude links to a neighbor that is not reachable at a NUD level */
if(nbr == NULL || nbr->state != NBR_REACHABLE) {
return 0;
}
#endif /* UIP_CONF_ND6_SEND_NA */
#endif /* UIP_ND6_SEND_NS */
/* If we don't have fresh link information, assume the parent is reachable. */
return !rpl_parent_is_fresh(p) || p->dag->instance->of->parent_has_usable_link(p);
}
@ -309,7 +309,7 @@ nullify_parents(rpl_dag_t *dag, rpl_rank_t minimum_rank)
}
/*---------------------------------------------------------------------------*/
static int
should_send_dao(rpl_instance_t *instance, rpl_dio_t *dio, rpl_parent_t *p)
should_refresh_routes(rpl_instance_t *instance, rpl_dio_t *dio, rpl_parent_t *p)
{
/* if MOP is set to no downward routes no DAO should be sent */
if(instance->mop == RPL_MOP_NO_DOWNWARD_ROUTES) {
@ -827,12 +827,16 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
PRINTF("RPL: Changed preferred parent, rank changed from %u to %u\n",
(unsigned)old_rank, best_dag->rank);
RPL_STAT(rpl_stats.parent_switch++);
if(RPL_IS_STORING(instance) && last_parent != NULL) {
/* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME);
if(RPL_IS_STORING(instance)) {
if(last_parent != NULL) {
/* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME);
}
/* Trigger DAO transmission from immediate children.
* Only for storing mode, see RFC6550 section 9.6. */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
}
/* The DAO parent set changed - schedule a DAO transmission. */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
rpl_schedule_dao(instance);
rpl_reset_dio_timer(instance);
#if DEBUG
@ -861,7 +865,10 @@ best_parent(rpl_dag_t *dag, int fresh_only)
for(p = nbr_table_head(rpl_parents); p != NULL; p = nbr_table_next(rpl_parents, p)) {
/* Exclude parents from other DAGs or announcing an infinite rank */
if(p->dag != dag || p->rank == INFINITE_RANK) {
if(p->dag != dag || p->rank == INFINITE_RANK || p->rank < ROOT_RANK(dag->instance)) {
if(p->rank < ROOT_RANK(dag->instance)) {
PRINTF("RPL: Parent has invalid rank\n");
}
continue;
}
@ -870,7 +877,7 @@ best_parent(rpl_dag_t *dag, int fresh_only)
continue;
}
#ifndef UIP_CONF_ND6_SEND_NA
#if UIP_ND6_SEND_NS
{
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
/* Exclude links to a neighbor that is not reachable at a NUD level */
@ -878,7 +885,7 @@ best_parent(rpl_dag_t *dag, int fresh_only)
continue;
}
}
#endif /* UIP_CONF_ND6_SEND_NA */
#endif /* UIP_ND6_SEND_NS */
/* Now we have an acceptable parent, check if it is the new best */
best = of->best_parent(best, p);
@ -1318,8 +1325,12 @@ rpl_local_repair(rpl_instance_t *instance)
instance->has_downward_route = 0;
rpl_reset_dio_timer(instance);
/* Request refresh of DAO registrations next DIO */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
if(RPL_IS_STORING(instance)) {
/* Request refresh of DAO registrations next DIO. Only for storing mode. In
* non-storing mode, non-root nodes increment DTSN only on when their parent do,
* or on global repair (see RFC6550 section 9.6.) */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
}
RPL_STAT(rpl_stats.local_repairs++);
}
@ -1534,7 +1545,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
}
/* The DIO comes from a valid DAG, we can refresh its lifetime */
dag->lifetime = (1UL << (instance->dio_intmin + instance->dio_intdoubl)) / 1000;
dag->lifetime = (1UL << (instance->dio_intmin + instance->dio_intdoubl)) * RPL_DAG_LIFETIME / 1000;
PRINTF("Set dag ");
PRINT6ADDR(&dag->dag_id);
PRINTF(" lifetime to %ld\n", dag->lifetime);
@ -1604,7 +1615,9 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
/* We don't use route control, so we can have only one official parent. */
if(dag->joined && p == dag->preferred_parent) {
if(should_send_dao(instance, dio, p)) {
if(should_refresh_routes(instance, dio, p)) {
/* Our parent is requesting a new DAO. Increment DTSN in turn,
* in both storing and non-storing mode (see RFC6550 section 9.6.) */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
rpl_schedule_dao(instance);
}

View file

@ -201,14 +201,6 @@ rpl_icmp6_update_nbr_table(uip_ipaddr_t *from, nbr_table_reason_t reason, void *
}
}
if(nbr != NULL) {
#if UIP_ND6_SEND_NA
/* set reachable timer if we added or found the nbr entry - and update
neighbor entry to reachable to avoid sending NS/NA, etc. */
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
nbr->state = NBR_REACHABLE;
#endif /* UIP_ND6_SEND_NA */
}
return nbr;
}
/*---------------------------------------------------------------------------*/

View file

@ -145,8 +145,14 @@
/* Special value indicating immediate removal. */
#define RPL_ZERO_LIFETIME 0
/* Special value indicating infinite lifetime. */
#define RPL_INFINITE_LIFETIME 0xFF
#define RPL_ROUTE_INFINITE_LIFETIME 0xFFFFFFFF
#define RPL_LIFETIME(instance, lifetime) \
((unsigned long)(instance)->lifetime_unit * (lifetime))
(((lifetime) == RPL_INFINITE_LIFETIME) ? RPL_ROUTE_INFINITE_LIFETIME : (unsigned long)(instance)->lifetime_unit * (lifetime))
#ifndef RPL_CONF_MIN_HOPRANKINC
/* RFC6550 defines the default MIN_HOPRANKINC as 256.

View file

@ -174,7 +174,7 @@ handle_dio_timer(void *ptr)
if(instance->dio_send) {
/* send DIO if counter is less than desired redundancy */
if(instance->dio_redundancy != 0 && instance->dio_counter < instance->dio_redundancy) {
if(instance->dio_redundancy == 0 || instance->dio_counter < instance->dio_redundancy) {
#if RPL_CONF_STATS
instance->dio_totsend++;
#endif /* RPL_CONF_STATS */
@ -238,7 +238,7 @@ set_dao_lifetime_timer(rpl_instance_t *instance)
/* Set up another DAO within half the expiration time, if such a
time has been configured */
if(instance->lifetime_unit != 0xffff && instance->default_lifetime != 0xff) {
if(instance->default_lifetime != RPL_INFINITE_LIFETIME) {
clock_time_t expiration_time;
expiration_time = (clock_time_t)instance->default_lifetime *
(clock_time_t)instance->lifetime_unit *

View file

@ -122,7 +122,7 @@ rpl_purge_routes(void)
r = uip_ds6_route_head();
while(r != NULL) {
if(r->state.lifetime >= 1) {
if(r->state.lifetime >= 1 && r->state.lifetime != RPL_ROUTE_INFINITE_LIFETIME) {
/*
* If a route is at lifetime == 1, set it to 0, scheduling it for
* immediate removal below. This achieves the same as the original code,
@ -281,11 +281,11 @@ rpl_ipv6_neighbor_callback(uip_ds6_nbr_t *nbr)
PRINTF("RPL: Neighbor state changed for ");
PRINT6ADDR(&nbr->ipaddr);
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
#if UIP_ND6_SEND_NS || UIP_ND6_SEND_RA
PRINTF(", nscount=%u, state=%u\n", nbr->nscount, nbr->state);
#else /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
#else /* UIP_ND6_SEND_NS || UIP_ND6_SEND_RA */
PRINTF(", state=%u\n", nbr->state);
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
#endif /* UIP_ND6_SEND_NS || UIP_ND6_SEND_RA */
for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES; instance < end; ++instance) {
if(instance->used == 1 ) {
p = rpl_find_parent_any_dag(instance, &nbr->ipaddr);