Merge branch 'contiki'
Conflicts: cpu/cc26xx-cc13xx/lib/cc13xxware cpu/cc26xx-cc13xx/lib/cc26xxware
This commit is contained in:
commit
2f8549aaae
319 changed files with 58114 additions and 6745 deletions
|
@ -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;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @} */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
347
core/net/ipv6/websocket-http-client.c
Normal file
347
core/net/ipv6/websocket-http-client.c
Normal 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);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
123
core/net/ipv6/websocket-http-client.h
Normal file
123
core/net/ipv6/websocket-http-client.h
Normal 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
724
core/net/ipv6/websocket.c
Normal 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
113
core/net/ipv6/websocket.h
Normal 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 */
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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*/
|
||||
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 *)¶ms.src_addr, &linkaddr_node_addr);
|
||||
linkaddr_copy((linkaddr_t *)¶ms.src_addr,
|
||||
packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
|
||||
params.payload = packetbuf_dataptr();
|
||||
params.payload_len = packetbuf_datalen();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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, ¤t_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, ¤t_neighbor->addr, ¤t_asn)) {
|
||||
&frame, ¤t_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, ¤t_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, ¤t_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(¤t_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(¤t_asn, ×lot_diff, &backup_link);
|
||||
current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, ×lot_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(¤t_asn, ×lot_diff, &backup_link);
|
||||
current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, ×lot_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;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(), ¤t_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, ¤t_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], ¤t_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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue