Browse Source
Support for TinyDTLS (0.8.6) is added together an example at 'examples/dtls-echo'.pr/spi.typo

7 changed files with 1203 additions and 0 deletions
@ -0,0 +1,73 @@
|
||||
# name of your application
|
||||
APPLICATION = dtls_echo
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
BOARD_BLACKLIST := z1 wsn430-v1_4 wsn430-v1_3b waspmote-pro arduino-mega2560 \
|
||||
msb-430h msb-430 chronos telosb msbiot cc2538dk \
|
||||
saml21-xpro samr21-xpro arduino-duemilanove arduino-uno
|
||||
|
||||
BOARD_INSUFFICIENT_MEMORY := airfy-beacon chronos msb-430 msb-430h nrf51dongle \
|
||||
nrf6310 nucleo-f103 nucleo-f334 pca10000 pca10005 spark-core \
|
||||
stm32f0discovery telosb weio wsn430-v1_3b wsn430-v1_4 \
|
||||
yunjia-nrf51822 z1 nucleo-f072 cc2650stk nucleo-f030\
|
||||
nucleo-f070
|
||||
|
||||
# Include packages that pull up and auto-init the link layer.
|
||||
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
|
||||
USEMODULE += gnrc_netdev_default
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
# Specify the mandatory networking modules for IPv6 and sUDP
|
||||
USEMODULE += gnrc_ipv6_router_default
|
||||
USEMODULE += gnrc_udp
|
||||
# Add a routing protocol
|
||||
USEMODULE += gnrc_rpl
|
||||
# This application dumps received packets to STDIO using the pktdump module
|
||||
USEMODULE += gnrc_pktdump
|
||||
# Additional networking modules that can be dropped if not needed
|
||||
USEMODULE += gnrc_icmpv6_echo
|
||||
# Add also the shell, some shell commands
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += ps
|
||||
|
||||
#TinyDTLs (crypto.c) made use of pthread
|
||||
ifneq ($(BOARD),native)
|
||||
USEMODULE += pthread
|
||||
endif
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
CFLAGS += -DDEVELHELP
|
||||
|
||||
# NOTE: Add the package for TinyDTLS
|
||||
USEPKG += tinydtls
|
||||
|
||||
# NOTE: Those are taken from TinyDTLS. As the original Makefiles are
|
||||
# overwitten is a good idea to preserve them here.
|
||||
CFLAGS += -DDTLSv12 -DWITH_SHA256
|
||||
|
||||
# NOTE: This adds support for TLS_PSK_WITH_AES_128_CCM_8
|
||||
CFLAGS += -DDTLS_PSK
|
||||
|
||||
# NOTE: This adds support for TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
|
||||
CFLAGS += -DDTLS_ECC
|
||||
|
||||
# NOTE: If enabled TinyDTLS' log are disabled (if memory is a issue).
|
||||
# WARNING: Sometimes the log leads to Stack pointer corrupted.
|
||||
# The reason is not identified yet.
|
||||
# If said issue appears, enable this line.
|
||||
#CFLAGS += -DNDEBUG
|
||||
|
||||
# NOTE: The configuration for socket or non-socket communication in TinyDTLS.
|
||||
CFLAGS += -DWITH_RIOT_GNRC
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
include $(RIOTBASE)/Makefile.include |
@ -0,0 +1,77 @@
|
||||
# dtls_echo |
||||
|
||||
This example shows you how to use TinyDTLS with the non-socket approach. |
||||
|
||||
This code is based on ../gnrc_networking and ../gnrc_tftp. |
||||
Is a good idea to read their README.md's for any doubt of how making the |
||||
testings. |
||||
|
||||
## SOCKET vs. Non-socket (GNRC) |
||||
|
||||
This example is configured to use the GNRC instead of sockets (over GNRC). |
||||
At the moment, the configuration must be done manually in the Makefile of |
||||
this project. |
||||
|
||||
## Fast configuration (Between RIOT instances): |
||||
|
||||
Preparing the logical interfaces: |
||||
|
||||
./../../dist/tools/tapsetup/tapsetup --create 2 |
||||
|
||||
For the server instance: |
||||
|
||||
make all; PORT=tap1 make term |
||||
dtlss start |
||||
ifconfig |
||||
|
||||
Do not forget to copy the IPv6 addresses! |
||||
|
||||
For the client: |
||||
|
||||
PORT=tap0 make term |
||||
dtlsc <IPv6's server address> "DATA TO DATA TO DATA!" |
||||
|
||||
# Testings |
||||
## Boards |
||||
|
||||
Those boards that do not support the `../gnrc_networking` example are included |
||||
in the `BOARD_INSUFFICIENT_MEMORY`, plus the board `cc2650stk`. |
||||
|
||||
There are certain boards that are having issues with `crypto.c` and |
||||
`dtls_time.h` Which for now are in the the `BOARD_BLACKLIST`. |
||||
|
||||
The boards that requires `periph_conf.h` are not tested. |
||||
|
||||
Boards with problem type 1 (`crypto.c`): |
||||
z1 |
||||
wsn430-v1_4 |
||||
wsn430-v1_3b |
||||
waspmote-pro |
||||
msb-430h |
||||
msb-430 |
||||
chronos |
||||
arduino-mega2560 |
||||
|
||||
Boards with problem type 2 (`dtls_time.h`): |
||||
cc2538dk |
||||
msbiot |
||||
telosb |
||||
|
||||
Boards with problem type 3 (Redifinition): |
||||
saml21-xpro |
||||
samr21-xpro |
||||
arduino-uno |
||||
arduino-duemilanove |
||||
|
||||
NOTE: Those on type 1 can be benefit of the following PR: |
||||
https://github.com/RIOT-OS/RIOT/issues/2360 |
||||
However, there are still issues to fix. |
||||
|
||||
NOTE: Those on type 2 can be fixed with the patch at |
||||
https://github.com/RIOT-OS/RIOT/pull/5974 |
||||
|
||||
## FIT-LAB |
||||
|
||||
The code has been tested in the FIT-LAB with M3 motes. |
||||
However, erros can occurrs. Enabling the line `CFLAGS += -DNDEBUG` in |
||||
the `Makefile` reduces the risk. |
@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup examples |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief The cliend side of TinyDTLS (Simple echo) |
||||
* |
||||
* @author Raul A. Fuentes Samaniego <ra.fuentes.sam+RIOT@gmail.com> |
||||
* @author Olaf Bergmann <bergmann@tzi.org> |
||||
* @author Hauke Mehrtens <hauke@hauke-m.de> |
||||
* @author Oliver Hahm <oliver.hahm@inria.fr> |
||||
* @} |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <inttypes.h> |
||||
|
||||
#include "net/gnrc.h" |
||||
#include "net/gnrc/ipv6.h" |
||||
#include "net/gnrc/udp.h" |
||||
#include "net/gnrc/pktdump.h" |
||||
#include "timex.h" |
||||
#include "xtimer.h" |
||||
|
||||
#define ENABLE_DEBUG (1) |
||||
#include "debug.h" |
||||
|
||||
/* TinyDTLS */ |
||||
#include "tinydtls.h" |
||||
#include "dtls_debug.h" |
||||
#include "dtls.h" |
||||
#include "global.h" |
||||
|
||||
|
||||
|
||||
/* TODO: Remove the UNUSED_PARAM from TinyDTLS' stack? */ |
||||
#ifdef __GNUC__ |
||||
#define UNUSED_PARAM __attribute__((unused)) |
||||
#else |
||||
#define UNUSED_PARAM |
||||
#endif /* __GNUC__ */ |
||||
|
||||
#ifdef DTLS_PSK |
||||
|
||||
#define PSK_DEFAULT_IDENTITY "Client_identity" |
||||
#define PSK_DEFAULT_KEY "secretPSK" |
||||
#define PSK_OPTIONS "i:k:" |
||||
|
||||
/* Max size for PSK lowered for embedded devices */ |
||||
#define PSK_ID_MAXLEN 32 |
||||
#define PSK_MAXLEN 32 |
||||
|
||||
#endif /* DTLS_PSK */ |
||||
|
||||
//#define DEFAULT_PORT 20220 /* DTLS default port */
|
||||
#define DEFAULT_PORT 61618 /* First valid FEBx address */ |
||||
|
||||
#define CLIENT_PORT DEFAULT_PORT + 1 |
||||
#define MAX_TIMES_TRY_TO_SEND 10 |
||||
|
||||
static dtls_context_t *dtls_context = NULL; |
||||
static char *client_payload; |
||||
static size_t buflen = 0; |
||||
|
||||
static const unsigned char ecdsa_priv_key[] = { |
||||
0x41, 0xC1, 0xCB, 0x6B, 0x51, 0x24, 0x7A, 0x14, |
||||
0x43, 0x21, 0x43, 0x5B, 0x7A, 0x80, 0xE7, 0x14, |
||||
0x89, 0x6A, 0x33, 0xBB, 0xAD, 0x72, 0x94, 0xCA, |
||||
0x40, 0x14, 0x55, 0xA1, 0x94, 0xA9, 0x49, 0xFA |
||||
}; |
||||
|
||||
static const unsigned char ecdsa_pub_key_x[] = { |
||||
0x36, 0xDF, 0xE2, 0xC6, 0xF9, 0xF2, 0xED, 0x29, |
||||
0xDA, 0x0A, 0x9A, 0x8F, 0x62, 0x68, 0x4E, 0x91, |
||||
0x63, 0x75, 0xBA, 0x10, 0x30, 0x0C, 0x28, 0xC5, |
||||
0xE4, 0x7C, 0xFB, 0xF2, 0x5F, 0xA5, 0x8F, 0x52 |
||||
}; |
||||
|
||||
static const unsigned char ecdsa_pub_key_y[] = { |
||||
0x71, 0xA0, 0xD4, 0xFC, 0xDE, 0x1A, 0xB8, 0x78, |
||||
0x5A, 0x3C, 0x78, 0x69, 0x35, 0xA7, 0xCF, 0xAB, |
||||
0xE9, 0x3F, 0x98, 0x72, 0x09, 0xDA, 0xED, 0x0B, |
||||
0x4F, 0xAB, 0xC3, 0x6F, 0xC7, 0x72, 0xF8, 0x29 |
||||
}; |
||||
|
||||
|
||||
/**
|
||||
* @brief This care about getting messages and continue with the DTLS flights. |
||||
* This will handle all the packets arriving to the node. |
||||
* Will determine if its from a new DTLS peer or a previous one. |
||||
*/ |
||||
static void dtls_handle_read(dtls_context_t *ctx, gnrc_pktsnip_t *pkt) |
||||
{ |
||||
|
||||
static session_t session; |
||||
|
||||
/*
|
||||
* NOTE: GNRC (Non-socket) issue: we need to modify the current |
||||
* DTLS Context for the IPv6 src (and in a future the port src). |
||||
*/ |
||||
|
||||
/* Taken from the tftp server example */ |
||||
char addr_str[IPV6_ADDR_MAX_STR_LEN]; |
||||
gnrc_pktsnip_t *tmp2; |
||||
|
||||
tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); |
||||
ipv6_hdr_t *hdr = (ipv6_hdr_t *)tmp2->data; |
||||
|
||||
ipv6_addr_to_str(addr_str, &hdr->src, sizeof(addr_str)); |
||||
|
||||
/*
|
||||
*TODO: More testings with TinyDTLS is neccesary, but seem this is safe. |
||||
*/ |
||||
tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP); |
||||
udp_hdr_t *udp = (udp_hdr_t *)tmp2->data; |
||||
|
||||
session.size = sizeof(ipv6_addr_t) + sizeof(unsigned short); |
||||
session.port = byteorder_ntohs(udp->src_port); |
||||
|
||||
DEBUG("DBG-Client: Msg received from \n\t Addr Src: %s" |
||||
"\n\t Current Peer: %s \n", addr_str, (char *)dtls_get_app_data(ctx)); |
||||
|
||||
ipv6_addr_from_str(&session.addr, addr_str); |
||||
|
||||
dtls_handle_message(ctx, &session, pkt->data, (unsigned int)pkt->size); |
||||
|
||||
} |
||||
|
||||
#ifdef DTLS_PSK |
||||
static unsigned char psk_id[PSK_ID_MAXLEN] = PSK_DEFAULT_IDENTITY; |
||||
static size_t psk_id_length = sizeof(PSK_DEFAULT_IDENTITY) - 1; |
||||
static unsigned char psk_key[PSK_MAXLEN] = PSK_DEFAULT_KEY; |
||||
static size_t psk_key_length = sizeof(PSK_DEFAULT_KEY) - 1; |
||||
|
||||
/**
|
||||
* This function is the "key store" for tinyDTLS. It is called to |
||||
* retrieve a key for the given identity within this particular |
||||
* session. |
||||
*/ |
||||
static int peer_get_psk_info(struct dtls_context_t *ctx UNUSED_PARAM, |
||||
const session_t *session UNUSED_PARAM, |
||||
dtls_credentials_type_t type, |
||||
const unsigned char *id, size_t id_len, |
||||
unsigned char *result, size_t result_length) |
||||
{ |
||||
|
||||
switch (type) { |
||||
case DTLS_PSK_IDENTITY: |
||||
/* Removed due probably in the motes is useless
|
||||
if (id_len) { |
||||
dtls_debug("got psk_identity_hint: '%.*s'\n", id_len, id); |
||||
} |
||||
*/ |
||||
|
||||
if (result_length < psk_id_length) { |
||||
dtls_warn("cannot set psk_identity -- buffer too small\n"); |
||||
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); |
||||
} |
||||
|
||||
memcpy(result, psk_id, psk_id_length); |
||||
return psk_id_length; |
||||
case DTLS_PSK_KEY: |
||||
if (id_len != psk_id_length || memcmp(psk_id, id, id_len) != 0) { |
||||
dtls_warn("PSK for unknown id requested, exiting\n"); |
||||
return dtls_alert_fatal_create(DTLS_ALERT_ILLEGAL_PARAMETER); |
||||
} |
||||
else if (result_length < psk_key_length) { |
||||
dtls_warn("cannot set psk -- buffer too small\n"); |
||||
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); |
||||
} |
||||
|
||||
memcpy(result, psk_key, psk_key_length); |
||||
return psk_key_length; |
||||
default: |
||||
dtls_warn("unsupported request type: %d\n", type); |
||||
} |
||||
|
||||
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); |
||||
} |
||||
#endif /* DTLS_PSK */ |
||||
|
||||
|
||||
#ifdef DTLS_ECC |
||||
static int peer_get_ecdsa_key(struct dtls_context_t *ctx, |
||||
const session_t *session, |
||||
const dtls_ecdsa_key_t **result) |
||||
{ |
||||
(void) ctx; |
||||
(void) session; |
||||
|
||||
static const dtls_ecdsa_key_t ecdsa_key = { |
||||
.curve = DTLS_ECDH_CURVE_SECP256R1, |
||||
.priv_key = ecdsa_priv_key, |
||||
.pub_key_x = ecdsa_pub_key_x, |
||||
.pub_key_y = ecdsa_pub_key_y |
||||
}; |
||||
|
||||
*result = &ecdsa_key; |
||||
return 0; |
||||
} |
||||
|
||||
static int peer_verify_ecdsa_key(struct dtls_context_t *ctx, |
||||
const session_t *session, |
||||
const unsigned char *other_pub_x, |
||||
const unsigned char *other_pub_y, |
||||
size_t key_size) |
||||
{ |
||||
(void) ctx; |
||||
(void) session; |
||||
(void) key_size; |
||||
(void) other_pub_x; |
||||
(void) other_pub_y; |
||||
return 0; |
||||
} |
||||
#endif /* DTLS_ECC */ |
||||
|
||||
/**
|
||||
* @brief This will try to transmit using only GNRC stack. |
||||
* This is basically the original send function from gnrc/networking |
||||
*/ |
||||
static int gnrc_sending(char *addr_str, char *data, size_t data_len ) |
||||
{ |
||||
ipv6_addr_t addr; |
||||
gnrc_pktsnip_t *payload, *udp, *ip; |
||||
|
||||
/* parse destination address */ |
||||
if (ipv6_addr_from_str(&addr, addr_str) == NULL) { |
||||
puts("Error: unable to parse destination address"); |
||||
return -1; |
||||
} |
||||
|
||||
/* allocate payload */ |
||||
payload = gnrc_pktbuf_add(NULL, data, data_len, GNRC_NETTYPE_UNDEF); |
||||
|
||||
if (payload == NULL) { |
||||
puts("Error: unable to copy data to packet buffer"); |
||||
return -1; |
||||
} |
||||
|
||||
/* allocate UDP header */ |
||||
udp = gnrc_udp_hdr_build(payload, (uint16_t) CLIENT_PORT, (uint16_t) DEFAULT_PORT); |
||||
if (udp == NULL) { |
||||
puts("Error: unable to allocate UDP header"); |
||||
gnrc_pktbuf_release(payload); |
||||
return -1; |
||||
} |
||||
|
||||
/* allocate IPv6 header */ |
||||
ip = gnrc_ipv6_hdr_build(udp, NULL, &addr); |
||||
if (ip == NULL) { |
||||
puts("Error: unable to allocate IPv6 header"); |
||||
gnrc_pktbuf_release(udp); |
||||
return -1; |
||||
} |
||||
|
||||
/*
|
||||
* WARNING: Too fast and the nodes dies in middle of retransmissions. |
||||
* This issue appears in the FIT-Lab (m3 motes). |
||||
* In native, is not required. |
||||
*/ |
||||
xtimer_usleep(500000); |
||||
|
||||
/* send packet */ |
||||
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, ip)) { |
||||
puts("Error: unable to locate UDP thread"); |
||||
gnrc_pktbuf_release(ip); |
||||
return -1; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
/**
|
||||
* @brief This will print the data read from the Peer. |
||||
* THIS AT THE END of the 6 flights (DTLS App Data). |
||||
* Is here where we know that the connection has finished. |
||||
* TODO: The connected variable could de modified here. |
||||
*/ |
||||
static int read_from_peer(struct dtls_context_t *ctx, |
||||
session_t *session, uint8 *data, size_t len) |
||||
{ |
||||
/* Linux and Contiki version are exactly the same. */ |
||||
(void) session; |
||||
(void) ctx; |
||||
size_t i; |
||||
printf("\n\n Echo received: "); |
||||
for (i = 0; i < len; i++) |
||||
printf("%c", data[i]); |
||||
printf(" \n\n\n"); |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Will try to transmit the next DTLS flight for a speicifc Peer. |
||||
* NOTE:The global buff and buflen is used for be able to transmit the |
||||
* Payload in segmented datagrams. |
||||
*/ |
||||
static void try_send(struct dtls_context_t *ctx, session_t *dst) |
||||
{ |
||||
|
||||
int res; |
||||
|
||||
res = dtls_write(ctx, dst, (uint8_t *)client_payload, buflen); |
||||
|
||||
if (res >= 0) { |
||||
memmove(client_payload, client_payload + res, buflen - res); |
||||
buflen -= res; |
||||
} |
||||
else { |
||||
DEBUG("DBG-Client: dtls_write returned error!\n" ); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @brief This SIGNAL function will prepare the next DTLS flight to send. |
||||
*/ |
||||
static int send_to_peer(struct dtls_context_t *ctx, |
||||
session_t *session, uint8 *buf, size_t len) |
||||
{ |
||||
|
||||
(void) session; |
||||
/*
|
||||
* For this testing with GNR we are to extract the peer's addresses and |
||||
* making the connection from zero. |
||||
*/ |
||||
char *addr_str; |
||||
addr_str = (char *)dtls_get_app_data(ctx); |
||||
|
||||
|
||||
/* TODO: Confirm that indeed len size of data was sent, otherwise
|
||||
* return the correct number of bytes sent |
||||
*/ |
||||
gnrc_sending(addr_str, (char *)buf, len); |
||||
|
||||
return len; |
||||
} |
||||
|
||||
|
||||
|
||||
/***
|
||||
* This is a custom function for preparing the SIGNAL events and |
||||
* create a new DTLS context. |
||||
*/ |
||||
static void init_dtls(session_t *dst, char *addr_str) |
||||
{ |
||||
|
||||
static dtls_handler_t cb = { |
||||
.write = send_to_peer, |
||||
.read = read_from_peer, |
||||
.event = NULL, |
||||
#ifdef DTLS_PSK |
||||
.get_psk_info = peer_get_psk_info, |
||||
#endif /* DTLS_PSK */ |
||||
#ifdef DTLS_ECC |
||||
.get_ecdsa_key = peer_get_ecdsa_key, |
||||
.verify_ecdsa_key = peer_verify_ecdsa_key |
||||
#endif /* DTLS_ECC */ |
||||
}; |
||||
|
||||
#ifdef DTLS_PSK |
||||
puts("Client support PSK"); |
||||
#endif |
||||
#ifdef DTLS_ECC |
||||
puts("Client support ECC"); |
||||
#endif |
||||
|
||||
DEBUG("DBG-Client: Debug ON"); |
||||
/*
|
||||
* The objective of ctx->App is be able to retrieve |
||||
* enough information for restablishing a connection. |
||||
* This is to be used in the send_to_peer and (potentially) the |
||||
* read_from_peer, to continue the transmision with the next |
||||
* step of the DTLS flights. |
||||
* TODO: Take away the DEFAULT_PORT and CLIENT_PORT. |
||||
*/ |
||||
dst->size = sizeof(ipv6_addr_t) + sizeof(unsigned short); |
||||
dst->port = (unsigned short) DEFAULT_PORT; |
||||
|
||||
if (ipv6_addr_from_str(&dst->addr, addr_str) == NULL) { |
||||
puts("ERROR: init_dtls was unable to load the IPv6 addresses!\n"); |
||||
dtls_context = NULL; |
||||
return; |
||||
} |
||||
|
||||
ipv6_addr_t addr_dbg; |
||||
ipv6_addr_from_str(&addr_dbg, addr_str); |
||||
|
||||
/*akin to syslog: EMERG, ALERT, CRITC, NOTICE, INFO, DEBUG */ |
||||
dtls_set_log_level(DTLS_LOG_NOTICE); |
||||
|
||||
dtls_context = dtls_new_context(addr_str); |
||||
if (dtls_context) { |
||||
dtls_set_handler(dtls_context, &cb); |
||||
} |
||||
|
||||
return; |
||||
} |
||||
|
||||
/**
|
||||
* This is the "client" part of this program. |
||||
* Will be called each time a message is transmitted. |
||||
*/ |
||||
static void client_send(char *addr_str, char *data, unsigned int delay) |
||||
{ |
||||
static int8_t iWatch; |
||||
static session_t dst; |
||||
static int connected = 0; |
||||
msg_t msg; |
||||
|
||||
gnrc_netreg_entry_t entry = GNRC_NETREG_ENTRY_INIT_PID(CLIENT_PORT, |
||||
sched_active_pid); |
||||
dtls_init(); |
||||
|
||||
if (gnrc_netreg_register(GNRC_NETTYPE_UDP, &entry)) { |
||||
puts("Unable to register ports"); |
||||
/*FIXME: Release memory?*/ |
||||
return; |
||||
} |
||||
|
||||
if (strlen(data) > DTLS_MAX_BUF) { |
||||
puts("Data too long "); |
||||
return; |
||||
} |
||||
|
||||
init_dtls(&dst, addr_str); |
||||
if (!dtls_context) { |
||||
dtls_emerg("cannot create context\n"); |
||||
puts("Client unable to load context!"); |
||||
return; |
||||
} |
||||
|
||||
/* client_payload is global due to the SIGNAL function send_to_peer */ |
||||
client_payload = data; |
||||
buflen = strlen(client_payload); |
||||
iWatch = MAX_TIMES_TRY_TO_SEND; |
||||
|
||||
/*
|
||||
* dtls_connect is the one who begin all the process. |
||||
* However, do it too fast, and the node will attend it before having a |
||||
* valid IPv6 or even a route to the destiny (This could be verified as the |
||||
* sequence number of the first DTLS Hello message will be greater than |
||||
* zero). |
||||
*/ |
||||
//connected = dtls_connect(dtls_context, &dst) >= 0;
|
||||
|
||||
/*
|
||||
* Until all the data is not sent we remains trying to connect to the |
||||
* server. Plus a small watchdog. |
||||
*/ |
||||
while ((buflen > 0) && (iWatch > 0)) { |
||||
|
||||
/*
|
||||
* NOTE: I (rfuentess) personally think this should be until this point |
||||
* instead before (that is why the previous dtls_connect is by defualt |
||||
* commented.. |
||||
*/ |
||||
if (!connected) { |
||||
connected = dtls_connect(dtls_context, &dst); |
||||
} |
||||
else if (connected < 0) { |
||||
puts("Client DTLS was unable to establish a channel!\n"); |
||||
/*NOTE: Not sure what to do in this scenario (if can happens)*/ |
||||
} |
||||
else { |
||||
/*TODO: must happens always or only when connected?*/ |
||||
try_send(dtls_context, &dst); |
||||
} |
||||
|
||||
/*
|
||||
* WARNING: The delay is KEY HERE! Too fast, and we can kill the |
||||
* DTLS state machine. Another alternative is change to |
||||
* blocking states (making the watchdog useless) |
||||
* |
||||
* msg_receive(&msg); |
||||
*/ |
||||
|
||||
xtimer_usleep(delay); |
||||
|
||||
if (msg_try_receive(&msg) == 1) { |
||||
dtls_handle_read(dtls_context, (gnrc_pktsnip_t *)(msg.content.ptr)); |
||||
} |
||||
|
||||
iWatch--; |
||||
} /*END while*/ |
||||
|
||||
dtls_free_context(dtls_context); |
||||
/* unregister our UDP listener on this thread */ |
||||
gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &entry); |
||||
connected = 0; /*Probably this should be removed or global */ |
||||
|
||||
/* Permanent or not permanent? */ |
||||
DEBUG("DTLS-Client: DTLS session finished\n"); |
||||
} |
||||
|
||||
int udp_client_cmd(int argc, char **argv) |
||||
{ |
||||
if (argc < 2) { |
||||
printf("usage: %s <addr> <data> [<delay in us>]\n", argv[0]); |
||||
return 1; |
||||
} |
||||
|
||||
uint32_t delay = 1000000; |
||||
if (argc < 3) { |
||||
printf("usage: %s <addr> <data> [<delay in us>]\n", |
||||
argv[0]); |
||||
return 1; |
||||
} |
||||
if (argc > 3) { |
||||
delay = (uint32_t)atoi(argv[3]); |
||||
} |
||||
client_send(argv[1], argv[2], delay); |
||||
|
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup examples |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief The server side of TinyDTLS (Simple echo) |
||||
* |
||||
* @author Raul A. Fuentes Samaniego <ra.fuentes.sam+RIOT@gmail.com> |
||||
* @author Olaf Bergmann <bergmann@tzi.org> |
||||
* @author Hauke Mehrtens <hauke@hauke-m.de> |
||||
* @author Oliver Hahm <oliver.hahm@inria.fr> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <inttypes.h> |
||||
|
||||
#include "net/gnrc.h" |
||||
#include "net/gnrc/ipv6.h" |
||||
#include "net/gnrc/udp.h" |
||||
#include "timex.h" |
||||
#include "xtimer.h" |
||||
#include "msg.h" |
||||
|
||||
|
||||
/* TinyDTLS */ |
||||
#include "dtls.h" |
||||
#include "dtls_debug.h" |
||||
#include "tinydtls.h" |
||||
|
||||
#define ENABLE_DEBUG (1) |
||||
#include "debug.h" |
||||
|
||||
//#define DEFAULT_PORT 20220 /* DTLS default port */
|
||||
#define DEFAULT_PORT 61618 /* First valid FEBx address */ |
||||
|
||||
/* TODO: MAke this local! */ |
||||
static dtls_context_t *dtls_context = NULL; |
||||
|
||||
static const unsigned char ecdsa_priv_key[] = { |
||||
0xD9, 0xE2, 0x70, 0x7A, 0x72, 0xDA, 0x6A, 0x05, |
||||
0x04, 0x99, 0x5C, 0x86, 0xED, 0xDB, 0xE3, 0xEF, |
||||
0xC7, 0xF1, 0xCD, 0x74, 0x83, 0x8F, 0x75, 0x70, |
||||
0xC8, 0x07, 0x2D, 0x0A, 0x76, 0x26, 0x1B, 0xD4 |
||||
}; |
||||
|
||||
static const unsigned char ecdsa_pub_key_x[] = { |
||||
0xD0, 0x55, 0xEE, 0x14, 0x08, 0x4D, 0x6E, 0x06, |
||||
0x15, 0x59, 0x9D, 0xB5, 0x83, 0x91, 0x3E, 0x4A, |
||||
0x3E, 0x45, 0x26, 0xA2, 0x70, 0x4D, 0x61, 0xF2, |
||||
0x7A, 0x4C, 0xCF, 0xBA, 0x97, 0x58, 0xEF, 0x9A |
||||
}; |
||||
|
||||
static const unsigned char ecdsa_pub_key_y[] = { |
||||
0xB4, 0x18, 0xB6, 0x4A, 0xFE, 0x80, 0x30, 0xDA, |
||||
0x1D, 0xDC, 0xF4, 0xF4, 0x2E, 0x2F, 0x26, 0x31, |
||||
0xD0, 0x43, 0xB1, 0xFB, 0x03, 0xE2, 0x2F, 0x4D, |
||||
0x17, 0xDE, 0x43, 0xF9, 0xF9, 0xAD, 0xEE, 0x70 |
||||
}; |
||||
|
||||
|
||||
static gnrc_netreg_entry_t server = GNRC_NETREG_ENTRY_INIT_PID( |
||||
GNRC_NETREG_DEMUX_CTX_ALL, |
||||
KERNEL_PID_UNDEF); |
||||
|
||||
#define READER_QUEUE_SIZE (8U) |
||||
char _server_stack[THREAD_STACKSIZE_MAIN + THREAD_EXTRA_STACKSIZE_PRINTF]; |
||||
|
||||
static kernel_pid_t _dtls_kernel_pid; |
||||
|
||||
/**
|
||||
* @brief This care about getting messages and continue with the DTLS flights |
||||
*/ |
||||
static void dtls_handle_read(dtls_context_t *ctx, gnrc_pktsnip_t *pkt) |
||||
{ |
||||
|
||||
static session_t session; |
||||
|
||||
/*
|
||||
* NOTE: GNRC (Non-socket) issue: we need to modify the current |
||||
* DTLS Context for the IPv6 src (and in a future the port src). |
||||
*/ |
||||
|
||||
/* Taken from the tftp server example */ |
||||
char addr_str[IPV6_ADDR_MAX_STR_LEN]; |
||||
gnrc_pktsnip_t *tmp2; |
||||
|
||||
tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); |
||||
ipv6_hdr_t *hdr = (ipv6_hdr_t *)tmp2->data; |
||||
|
||||
ipv6_addr_to_str(addr_str, &hdr->src, sizeof(addr_str)); |
||||
/* This is unique to the server (Non-socket) */ |
||||
ctx->app = addr_str; |
||||
|
||||
/*
|
||||
* TODO: More testings with TinyDTLS is neccesary, but seem this is safe. |
||||
*/ |
||||
tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP); |
||||
udp_hdr_t *udp = (udp_hdr_t *)tmp2->data; |
||||
|
||||
session.size = sizeof(ipv6_addr_t) + sizeof(unsigned short); |
||||
session.port = byteorder_ntohs(udp->src_port); |
||||
|
||||
ipv6_addr_from_str(&session.addr, addr_str); |
||||
|
||||
dtls_handle_message(ctx, &session, pkt->data, (unsigned int)pkt->size); |
||||
|
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @brief We got the TinyDTLS App Data message and answer with the same |
||||
*/ |
||||
static int read_from_peer(struct dtls_context_t *ctx, |
||||
session_t *session, uint8 *data, size_t len) |
||||
{ |
||||
|
||||
|
||||
#if ENABLE_DEBUG == 1 |
||||
size_t i; |
||||
DEBUG("\nDBG-Server: Data from Client: ---"); |
||||
for (i = 0; i < len; i++) |
||||
DEBUG("%c", data[i]); |
||||
DEBUG("--- \t Sending echo..\n"); |
||||
#endif |
||||
/* echo incoming application data */ |
||||
dtls_write(ctx, session, data, len); |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* @brief This will try to transmit using only GNRC stack (non-socket). |
||||
*/ |
||||
static int gnrc_sending(char *addr_str, char *data, size_t data_len, unsigned short rem_port ) |
||||
{ |
||||
|
||||
ipv6_addr_t addr; |
||||
gnrc_pktsnip_t *payload, *udp, *ip; |
||||
|
||||
/* parse destination address */ |
||||
if (ipv6_addr_from_str(&addr, addr_str) == NULL) { |
||||
puts("Error: unable to parse destination address"); |
||||
return -1; |
||||
} |
||||
|
||||
payload = gnrc_pktbuf_add(NULL, data, data_len, GNRC_NETTYPE_UNDEF); |
||||
|
||||
if (payload == NULL) { |
||||
puts("Error: unable to copy data to packet buffer"); |
||||
return -1; |
||||
} |
||||
|
||||
/* allocate UDP header */ |
||||
udp = gnrc_udp_hdr_build(payload, DEFAULT_PORT, rem_port); |
||||
if (udp == NULL) { |
||||
puts("Error: unable to allocate UDP header"); |
||||
gnrc_pktbuf_release(payload); |
||||
return -1; |
||||
} |
||||
|
||||
/* allocate IPv6 header */ |
||||
ip = gnrc_ipv6_hdr_build(udp, NULL, &addr); |
||||
if (ip == NULL) { |
||||
puts("Error: unable to allocate IPv6 header"); |
||||
gnrc_pktbuf_release(udp); |
||||
return -1; |
||||
} |
||||
/* send packet */ |
||||
|
||||
DEBUG("DBG-Server: Sending record to peer\n"); |
||||
|
||||
/*
|
||||
* WARNING: Too fast and the nodes dies in middle of retransmissions. |
||||
* This issue appears in the FIT-Lab (m3 motes). |
||||
*/ |
||||
xtimer_usleep(500000); |
||||
|
||||
/* Probably this part will be removed. **/ |
||||
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, ip)) { |
||||
puts("Error: unable to locate UDP thread"); |
||||
gnrc_pktbuf_release(ip); |
||||
return -1; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
/**
|
||||
* @brief We communicate with the other peer. |
||||
*/ |
||||
static int send_to_peer(struct dtls_context_t *ctx, |
||||
session_t *session, uint8 *buf, size_t len) |
||||
{ |
||||
|
||||
(void) session; |
||||
|
||||
/*FIXME TODO: dtls_get_app_data(ctx) should have the remote port! */ |
||||
char *addr_str; |
||||
addr_str = (char *)dtls_get_app_data(ctx); |
||||
|
||||
gnrc_sending(addr_str, (char *)buf, len, session->port); |
||||
|
||||
return len; |
||||
} |
||||
|
||||
#ifdef DTLS_PSK |
||||
/* This function is the "key store" for tinyDTLS. It is called to
|
||||
* retrieve a key for the given identity within this particular |
||||
* session. */ |
||||
static int peer_get_psk_info(struct dtls_context_t *ctx, const session_t *session, |
||||
dtls_credentials_type_t type, |
||||
const unsigned char *id, size_t id_len, |
||||
unsigned char *result, size_t result_length) |
||||
{ |
||||
|
||||
(void) ctx; |
||||
(void) session; |
||||
struct keymap_t { |
||||
unsigned char *id; |
||||
size_t id_length; |
||||
unsigned char *key; |
||||
size_t key_length; |
||||
} psk[3] = { |
||||
{ (unsigned char *)"Client_identity", 15, |
||||
(unsigned char *)"secretPSK", 9 }, |
||||
{ (unsigned char *)"default identity", 16, |
||||
(unsigned char *)"\x11\x22\x33", 3 }, |
||||
{ (unsigned char *)"\0", 2, |
||||
(unsigned char *)"", 1 } |
||||
}; |
||||
|
||||
if (type != DTLS_PSK_KEY) { |
||||
return 0; |
||||
} |
||||
|
||||
if (id) { |
||||
unsigned int i; |
||||
for (i = 0; i < sizeof(psk) / sizeof(struct keymap_t); i++) { |
||||
if (id_len == psk[i].id_length && memcmp(id, psk[i].id, id_len) == 0) { |
||||
if (result_length < psk[i].key_length) { |
||||
dtls_warn("buffer too small for PSK"); |
||||
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); |
||||
} |
||||
|
||||
memcpy(result, psk[i].key, psk[i].key_length); |
||||
return psk[i].key_length; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return dtls_alert_fatal_create(DTLS_ALERT_DECRYPT_ERROR); |
||||
} |
||||
#endif /* DTLS_PSK */ |
||||
|
||||
#ifdef DTLS_ECC |
||||
static int peer_get_ecdsa_key(struct dtls_context_t *ctx, |
||||
const session_t *session, |
||||
const dtls_ecdsa_key_t **result) |
||||
{ |
||||
(void) ctx; |
||||
(void) session; |
||||
static const dtls_ecdsa_key_t ecdsa_key = { |
||||
.curve = DTLS_ECDH_CURVE_SECP256R1, |
||||
.priv_key = ecdsa_priv_key, |
||||
.pub_key_x = ecdsa_pub_key_x, |
||||
.pub_key_y = ecdsa_pub_key_y |
||||
}; |
||||
|
||||
*result = &ecdsa_key; |
||||
return 0; |
||||
} |
||||
|
||||
static int peer_verify_ecdsa_key(struct dtls_context_t *ctx, |
||||
const session_t *session, |
||||
const unsigned char *other_pub_x, |
||||
const unsigned char *other_pub_y, |
||||
size_t key_size) |
||||
{ |
||||
(void) ctx; |
||||
(void) session; |
||||
(void) other_pub_x; |
||||
(void) other_pub_y; |
||||
(void) key_size; |
||||
return 0; |
||||
} |
||||
#endif /* DTLS_ECC */ |
||||
|
||||
/**
|
||||
* @brief We prepare the DTLS for this node. |
||||
*/ |
||||
static void init_dtls(void) |
||||
{ |
||||
static dtls_handler_t cb = { |
||||
.write = send_to_peer, |
||||
.read = read_from_peer, |
||||
.event = NULL, |
||||
#ifdef DTLS_PSK |
||||
.get_psk_info = peer_get_psk_info, |
||||
#endif /* DTLS_PSK */ |
||||
#ifdef DTLS_ECC |
||||
.get_ecdsa_key = peer_get_ecdsa_key, |
||||
.verify_ecdsa_key = peer_verify_ecdsa_key |
||||
#endif /* DTLS_ECC */ |
||||
}; |
||||
|
||||
|
||||
#ifdef DTLS_PSK |
||||
puts("Server support PSK"); |
||||
#endif |
||||
#ifdef DTLS_ECC |
||||
puts("Server support ECC"); |
||||
#endif |
||||
|
||||
DEBUG("DBG-Server On\n"); |
||||
|
||||
/*
|
||||
* The context for the server is a little different from the client. |
||||
* The simplicity of GNRC do not mix transparently with |
||||
* the DTLS Context. At this point, the server need a fresh context |
||||
* however dtls_context->app must be populated with an unknown |
||||
* IPv6 address. |
||||
* |
||||
* The non-valid Ipv6 address ( :: ) is discarded due the chaos. |
||||
* For now, the first value will be the loopback. |
||||
*/ |
||||
char *addr_str = "::1"; |
||||
|
||||
/*akin to syslog: EMERG, ALERT, CRITC, NOTICE, INFO, DEBUG */ |
||||
dtls_set_log_level(DTLS_LOG_DEBUG); |
||||
|
||||
|
||||
dtls_context = dtls_new_context(addr_str); |
||||
if (dtls_context) { |
||||
dtls_set_handler(dtls_context, &cb); |
||||
} |
||||
else { |
||||
puts("Server was unable to generate DTLS Context!"); |
||||
exit(-1); |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
|
||||
/* NOTE: wrapper or trampoline ? (Syntax question) */ |
||||
|
||||
void *dtls_server_wrapper(void *arg) |
||||
{ |
||||
(void) arg; /* TODO: Remove? We don't have args at all (NULL) */ |
||||
|
||||
msg_t _reader_queue[READER_QUEUE_SIZE]; |
||||
msg_t msg; |
||||
|
||||
/* The GNRC examples uses packet dump but we want a custom one */ |
||||
msg_init_queue(_reader_queue, READER_QUEUE_SIZE); |
||||
|
||||
init_dtls(); |
||||
|
||||
/*
|
||||
* FIXME: After mutliple retransmissions, and canceled client's sessions |
||||
* the server become unable to sent NDP NA messages. Still, the TinyDTLS |
||||
* debugs seems to be fine. |
||||
*/ |
||||
|
||||
while (1) { |
||||
|
||||
/* wait for a message */ |
||||
msg_receive(&msg); |
||||
|
||||
DEBUG("DBG-Server: Record Rcvd!\n"); |
||||
dtls_handle_read(dtls_context, (gnrc_pktsnip_t *)(msg.content.ptr)); |
||||
|
||||
/*TODO: What happens with other clients connecting at the same time? */ |
||||
|
||||
} /*While */ |
||||
|
||||
dtls_free_context(dtls_context); |
||||
} |
||||
|
||||
static void start_server(void) |
||||
{ |
||||
uint16_t port; |
||||
|
||||
port = (uint16_t)DEFAULT_PORT; |
||||
|
||||
(void) _dtls_kernel_pid; |
||||
|
||||
/* Only one instance of the server */ |
||||
if (server.target.pid != KERNEL_PID_UNDEF) { |
||||
printf("Error: server already running\n"); |
||||
return; |
||||
} |
||||
|
||||
/*TESTING tinydtls*/ |
||||
dtls_init(); |
||||
|
||||
/* The server is initialized */ |
||||
server.target.pid = thread_create(_server_stack, sizeof(_server_stack), |
||||
THREAD_PRIORITY_MAIN - 1, |
||||
THREAD_CREATE_STACKTEST, |
||||
dtls_server_wrapper, NULL, "DTLS Server"); |
||||
|
||||
server.demux_ctx = (uint32_t)port; |
||||
|
||||
if (gnrc_netreg_register(GNRC_NETTYPE_UDP, &server) == 0) |
||||
printf("Success: started DTLS server on port %" PRIu16 "\n", port); |
||||
else |
||||
printf("FAILURE: The UDP port is not registered!\n"); |
||||
} |
||||
|
||||
static void stop_server(void) |
||||
{ |
||||
/* check if server is running at all */ |
||||
if (server.target.pid == KERNEL_PID_UNDEF) { |
||||
printf("Error: server was not running\n"); |
||||
return; |
||||
} |
||||
|
||||
dtls_free_context(dtls_context); |
||||
|
||||
/* stop server */ |
||||
gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &server); |
||||
server.target.pid = KERNEL_PID_UNDEF; |
||||
puts("Success: stopped DTLS server"); |
||||
} |
||||
|
||||
int udp_server_cmd(int argc, char **argv) |
||||
{ |
||||
if (argc < 1) { |
||||
printf("usage: %s start|stop\n", argv[0]); |
||||
return 1; |
||||
} |
||||
if (strcmp(argv[1], "start") == 0) { |
||||
start_server(); |
||||
} |
||||
else if (strcmp(argv[1], "stop") == 0) { |
||||
stop_server(); |
||||
} |
||||
else { |
||||
puts("error: invalid command"); |
||||
} |
||||
return 0; |
||||
} |
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
|
||||
/**
|
||||
* @ingroup examples |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Example application for TinyDTLS |
||||
* |
||||
* @author Raul Fuentes <> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
|
||||
#include <stdio.h> |
||||
|
||||
#include "shell.h" |
||||
#include "msg.h" |
||||
|
||||
|
||||
|
||||
/*TinyDTLS WARNING check*/ |
||||
#ifdef WITH_RIOT_SOCKETS |
||||
#error TinyDTLS is configured for working with Sockets. Yet, this is non-socket |
||||
#endif |
||||
|
||||
#define MAIN_QUEUE_SIZE (8) |
||||
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; |
||||
|
||||
/*
|
||||
* Altough the server and client cna be in a simple file. |
||||
* Is more friendly to divide them |
||||
*/ |
||||
extern int udp_client_cmd(int argc, char **argv); |
||||
extern int udp_server_cmd(int argc, char **argv); |
||||
|
||||
static const shell_command_t shell_commands[] = { |
||||
{ "dtlsc", "Start a DTLS client", udp_client_cmd }, |
||||
{ "dtlss", "Start a DTLS server (with echo)", udp_server_cmd }, |
||||
{ NULL, NULL, NULL } |
||||
}; |
||||
|
||||
int main(void) |
||||
{ |
||||
/* we need a message queue for the thread running the shell in order to
|
||||
* receive potentially fast incoming networking packets */ |
||||
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); |
||||
puts("RIOT (Tiny)DTLS testing implementation"); |
||||
|
||||
/* start shell */ |
||||
puts("All up, running the shell now"); |
||||
char line_buf[SHELL_DEFAULT_BUFSIZE]; |
||||
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); |
||||
|
||||
/* should be never reached */ |
||||
return 0; |
||||
} |
@ -0,0 +1,12 @@
|
||||
PKG_NAME=tinydtls
|
||||
PKG_URL=git://github.com/rfuentess/TinyDTLS.git
|
||||
# PKG_VERSION=RIOT-OS
|
||||
PKG_VERSION=f824b5553a865c186a9b41236be03358f0c8feaf
|
||||
PKG_BUILDDIR ?= $(BINDIRBASE)/pkg/$(BOARD)/$(PKG_NAME)
|
||||
|
||||
.PHONY: all |
||||
|
||||
all: git-download |
||||
$(MAKE) -C $(PKG_BUILDDIR)
|
||||
|
||||
include $(RIOTBASE)/pkg/pkg.mk |
Loading…
Reference in new issue