#include "netif/lowpan6_ble.h"
#if LWIP_IPV6
#include "lwip/ip.h"
#include "lwip/pbuf.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/nd6.h"
#include "lwip/mem.h"
#include "lwip/udp.h"
#include "lwip/tcpip.h"
#include "lwip/snmp.h"
#include <string.h>
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
static ip6_addr_t rfc7668_context[LWIP_6LOWPAN_NUM_CONTEXTS];
#else
#define rfc7668_context NULL
#endif
static struct lowpan6_link_addr rfc7668_local_addr;
static struct lowpan6_link_addr rfc7668_peer_addr;
void
ble_addr_to_eui64(u8_t *dst, const u8_t *src, int public_addr)
{
memcpy(dst, src, 3);
dst[3] = 0xFF;
dst[4] = 0xFE;
memcpy(&dst[5], &src[3], 3);
#if LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
if(public_addr) {
dst[0] &= ~0x02;
} else {
dst[0] |= 0x02;
}
#else
LWIP_UNUSED_ARG(public_addr);
#endif
}
void
eui64_to_ble_addr(u8_t *dst, const u8_t *src)
{
memcpy(dst,src,3);
memcpy(&dst[3],&src[5],3);
}
static err_t
rfc7668_set_addr(struct lowpan6_link_addr *addr, const u8_t *in_addr, size_t in_addr_len, int is_mac_48, int is_public_addr)
{
if ((in_addr == NULL) || (addr == NULL)) {
return ERR_VAL;
}
if (is_mac_48) {
if (in_addr_len != 6) {
return ERR_VAL;
}
addr->addr_len = 8;
ble_addr_to_eui64(addr->addr, in_addr, is_public_addr);
} else {
if (in_addr_len != 8) {
return ERR_VAL;
}
addr->addr_len = 8;
memcpy(addr->addr, in_addr, 8);
}
return ERR_OK;
}
err_t
rfc7668_set_local_addr_eui64(struct netif *netif, const u8_t *local_addr, size_t local_addr_len)
{
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_local_addr, local_addr, local_addr_len, 0, 0);
}
err_t
rfc7668_set_local_addr_mac48(struct netif *netif, const u8_t *local_addr, size_t local_addr_len, int is_public_addr)
{
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_local_addr, local_addr, local_addr_len, 1, is_public_addr);
}
err_t
rfc7668_set_peer_addr_eui64(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len)
{
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_peer_addr, peer_addr, peer_addr_len, 0, 0);
}
err_t
rfc7668_set_peer_addr_mac48(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len, int is_public_addr)
{
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_peer_addr, peer_addr, peer_addr_len, 1, is_public_addr);
}
static err_t
rfc7668_compress(struct netif *netif, struct pbuf *p)
{
struct pbuf *p_frag;
u16_t remaining_len;
u8_t *buffer;
u8_t lowpan6_header_len;
u8_t hidden_header_len;
err_t err;
LWIP_ASSERT("lowpan6_frag: netif->linkoutput not set", netif->linkoutput != NULL);
#if LWIP_6LOWPAN_IPHC
p_frag = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
if (p_frag == NULL) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
return ERR_MEM;
}
LWIP_ASSERT("this needs a pbuf in one piece", p_frag->len == p_frag->tot_len);
buffer = (u8_t*)p_frag->payload;
err = lowpan6_compress_headers(netif, (u8_t *)p->payload, p->len, buffer, p_frag->len,
&lowpan6_header_len, &hidden_header_len, rfc7668_context, &rfc7668_local_addr, &rfc7668_peer_addr);
if (err != ERR_OK) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
pbuf_free(p_frag);
return err;
}
pbuf_remove_header(p, hidden_header_len);
remaining_len = p->tot_len;
pbuf_copy_partial(p, buffer + lowpan6_header_len, remaining_len, 0);
p_frag->len = p_frag->tot_len = remaining_len + lowpan6_header_len;
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
LWIP_DEBUGF(LWIP_LOWPAN6_DEBUG|LWIP_DBG_TRACE, ("rfc7668_output: sending packet %p\n", (void *)p));
err = netif->linkoutput(netif, p_frag);
pbuf_free(p_frag);
return err;
#else
return ERR_IF;
#endif
}
err_t
rfc7668_set_context(u8_t idx, const ip6_addr_t *context)
{
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
if (idx >= LWIP_6LOWPAN_NUM_CONTEXTS) {
return ERR_ARG;
}
ip6_addr_set(&rfc7668_context[idx], context);
return ERR_OK;
#else
LWIP_UNUSED_ARG(idx);
LWIP_UNUSED_ARG(context);
return ERR_VAL;
#endif
}
err_t
rfc7668_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
{
LWIP_UNUSED_ARG(ip6addr);
return rfc7668_compress(netif, q);
}
err_t
rfc7668_input(struct pbuf * p, struct netif *netif)
{
u8_t * puc;
MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
puc = (u8_t*)p->payload;
if (*puc == 0x41) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, removing dispatch: 0x%2x \n", *puc));
pbuf_remove_header(p, 1);
} else if ((*puc & 0xe0 )== 0x60) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, decompress dispatch: 0x%2x \n", *puc));
p = lowpan6_decompress(p, 0, rfc7668_context, &rfc7668_peer_addr, &rfc7668_local_addr);
if (p == NULL) {
MIB2_STATS_NETIF_INC(netif, ifindiscards);
return ERR_OK;
}
} else {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, discarding: 0x%2x \n", *puc));
MIB2_STATS_NETIF_INC(netif, ifindiscards);
pbuf_free(p);
return ERR_OK;
}
MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
#if LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG
{
u16_t i;
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("IPv6 payload:\n"));
for (i = 0; i < p->len; i++) {
if ((i%4)==0) {
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("\n"));
}
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("%2X ", *((u8_t *)p->payload+i)));
}
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("\np->len: %d\n", p->len));
}
#endif
return ip6_input(p, netif);
}
err_t
rfc7668_if_init(struct netif *netif)
{
netif->name[0] = 'b';
netif->name[1] = 't';
netif->output_ip6 = rfc7668_output;
MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
netif->mtu = IP6_MIN_MTU_LENGTH;
netif->flags = 0;
return ERR_OK;
}
#if !NO_SYS
err_t
tcpip_rfc7668_input(struct pbuf *p, struct netif *inp)
{
return tcpip_inpkt(p, inp, rfc7668_input);
}
#endif
#endif