1195972f6Sopenharmony_ci/**
2195972f6Sopenharmony_ci * @file
3195972f6Sopenharmony_ci *
4195972f6Sopenharmony_ci * @defgroup zepif ZEP - ZigBee Encapsulation Protocol
5195972f6Sopenharmony_ci * @ingroup netifs
6195972f6Sopenharmony_ci * A netif implementing the ZigBee Encapsulation Protocol (ZEP).
7195972f6Sopenharmony_ci * This is used to tunnel 6LowPAN over UDP.
8195972f6Sopenharmony_ci *
9195972f6Sopenharmony_ci * Usage (there must be a default netif before!):
10195972f6Sopenharmony_ci * @code{.c}
11195972f6Sopenharmony_ci *   netif_add(&zep_netif, NULL, NULL, NULL, NULL, zepif_init, tcpip_6lowpan_input);
12195972f6Sopenharmony_ci *   netif_create_ip6_linklocal_address(&zep_netif, 1);
13195972f6Sopenharmony_ci *   netif_set_up(&zep_netif);
14195972f6Sopenharmony_ci *   netif_set_link_up(&zep_netif);
15195972f6Sopenharmony_ci * @endcode
16195972f6Sopenharmony_ci */
17195972f6Sopenharmony_ci
18195972f6Sopenharmony_ci/*
19195972f6Sopenharmony_ci * Copyright (c) 2018 Simon Goldschmidt
20195972f6Sopenharmony_ci * All rights reserved.
21195972f6Sopenharmony_ci *
22195972f6Sopenharmony_ci * Redistribution and use in source and binary forms, with or without modification,
23195972f6Sopenharmony_ci * are permitted provided that the following conditions are met:
24195972f6Sopenharmony_ci *
25195972f6Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice,
26195972f6Sopenharmony_ci *    this list of conditions and the following disclaimer.
27195972f6Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice,
28195972f6Sopenharmony_ci *    this list of conditions and the following disclaimer in the documentation
29195972f6Sopenharmony_ci *    and/or other materials provided with the distribution.
30195972f6Sopenharmony_ci * 3. The name of the author may not be used to endorse or promote products
31195972f6Sopenharmony_ci *    derived from this software without specific prior written permission.
32195972f6Sopenharmony_ci *
33195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
34195972f6Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
35195972f6Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
36195972f6Sopenharmony_ci * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37195972f6Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
38195972f6Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39195972f6Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40195972f6Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41195972f6Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
42195972f6Sopenharmony_ci * OF SUCH DAMAGE.
43195972f6Sopenharmony_ci *
44195972f6Sopenharmony_ci * This file is part of the lwIP TCP/IP stack.
45195972f6Sopenharmony_ci *
46195972f6Sopenharmony_ci * Author: Simon Goldschmidt <goldsimon@gmx.de>
47195972f6Sopenharmony_ci *
48195972f6Sopenharmony_ci */
49195972f6Sopenharmony_ci
50195972f6Sopenharmony_ci#include "netif/zepif.h"
51195972f6Sopenharmony_ci
52195972f6Sopenharmony_ci#if LWIP_IPV6 && LWIP_UDP
53195972f6Sopenharmony_ci
54195972f6Sopenharmony_ci#include "netif/lowpan6.h"
55195972f6Sopenharmony_ci#include "lwip/udp.h"
56195972f6Sopenharmony_ci#include "lwip/timeouts.h"
57195972f6Sopenharmony_ci#include <string.h>
58195972f6Sopenharmony_ci
59195972f6Sopenharmony_ci/** Define this to 1 to loop back TX packets for testing */
60195972f6Sopenharmony_ci#ifndef ZEPIF_LOOPBACK
61195972f6Sopenharmony_ci#define ZEPIF_LOOPBACK    0
62195972f6Sopenharmony_ci#endif
63195972f6Sopenharmony_ci
64195972f6Sopenharmony_ci#define ZEP_MAX_DATA_LEN  127
65195972f6Sopenharmony_ci
66195972f6Sopenharmony_ci#ifdef PACK_STRUCT_USE_INCLUDES
67195972f6Sopenharmony_ci#  include "arch/bpstruct.h"
68195972f6Sopenharmony_ci#endif
69195972f6Sopenharmony_ciPACK_STRUCT_BEGIN
70195972f6Sopenharmony_cistruct zep_hdr {
71195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t prot_id[2]);
72195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t prot_version);
73195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t type);
74195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t channel_id);
75195972f6Sopenharmony_ci  PACK_STRUCT_FIELD(u16_t device_id);
76195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t crc_mode);
77195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t unknown_1);
78195972f6Sopenharmony_ci  PACK_STRUCT_FIELD(u32_t timestamp[2]);
79195972f6Sopenharmony_ci  PACK_STRUCT_FIELD(u32_t seq_num);
80195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t unknown_2[10]);
81195972f6Sopenharmony_ci  PACK_STRUCT_FLD_8(u8_t len);
82195972f6Sopenharmony_ci} PACK_STRUCT_STRUCT;
83195972f6Sopenharmony_ciPACK_STRUCT_END
84195972f6Sopenharmony_ci#ifdef PACK_STRUCT_USE_INCLUDES
85195972f6Sopenharmony_ci#  include "arch/epstruct.h"
86195972f6Sopenharmony_ci#endif
87195972f6Sopenharmony_ci
88195972f6Sopenharmony_cistruct zepif_state {
89195972f6Sopenharmony_ci  struct zepif_init init;
90195972f6Sopenharmony_ci  struct udp_pcb *pcb;
91195972f6Sopenharmony_ci  u32_t seqno;
92195972f6Sopenharmony_ci};
93195972f6Sopenharmony_ci
94195972f6Sopenharmony_cistatic u8_t zep_lowpan_timer_running;
95195972f6Sopenharmony_ci
96195972f6Sopenharmony_ci/* Helper function that calls the 6LoWPAN timer and reschedules itself */
97195972f6Sopenharmony_cistatic void
98195972f6Sopenharmony_cizep_lowpan_timer(void *arg)
99195972f6Sopenharmony_ci{
100195972f6Sopenharmony_ci  lowpan6_tmr();
101195972f6Sopenharmony_ci  if (zep_lowpan_timer_running) {
102195972f6Sopenharmony_ci    sys_timeout(LOWPAN6_TMR_INTERVAL, zep_lowpan_timer, arg);
103195972f6Sopenharmony_ci  }
104195972f6Sopenharmony_ci}
105195972f6Sopenharmony_ci
106195972f6Sopenharmony_ci/* Pass received pbufs into 6LowPAN netif */
107195972f6Sopenharmony_cistatic void
108195972f6Sopenharmony_cizepif_udp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
109195972f6Sopenharmony_ci               const ip_addr_t *addr, u16_t port)
110195972f6Sopenharmony_ci{
111195972f6Sopenharmony_ci  err_t err;
112195972f6Sopenharmony_ci  struct netif *netif_lowpan6 = (struct netif *)arg;
113195972f6Sopenharmony_ci  struct zep_hdr *zep;
114195972f6Sopenharmony_ci
115195972f6Sopenharmony_ci  LWIP_ASSERT("arg != NULL", arg != NULL);
116195972f6Sopenharmony_ci  LWIP_ASSERT("pcb != NULL", pcb != NULL);
117195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(pcb); /* for LWIP_NOASSERT */
118195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(addr);
119195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(port);
120195972f6Sopenharmony_ci  if (p == NULL) {
121195972f6Sopenharmony_ci    return;
122195972f6Sopenharmony_ci  }
123195972f6Sopenharmony_ci
124195972f6Sopenharmony_ci  /* Parse and hide the ZEP header */
125195972f6Sopenharmony_ci  if (p->len < sizeof(struct zep_hdr)) {
126195972f6Sopenharmony_ci    /* need the zep_hdr in one piece */
127195972f6Sopenharmony_ci    goto err_return;
128195972f6Sopenharmony_ci  }
129195972f6Sopenharmony_ci  zep = (struct zep_hdr *)p->payload;
130195972f6Sopenharmony_ci  if (zep->prot_id[0] != 'E') {
131195972f6Sopenharmony_ci    goto err_return;
132195972f6Sopenharmony_ci  }
133195972f6Sopenharmony_ci  if (zep->prot_id[1] != 'X') {
134195972f6Sopenharmony_ci    goto err_return;
135195972f6Sopenharmony_ci  }
136195972f6Sopenharmony_ci  if (zep->prot_version != 2) {
137195972f6Sopenharmony_ci    /* we only support this version for now */
138195972f6Sopenharmony_ci    goto err_return;
139195972f6Sopenharmony_ci  }
140195972f6Sopenharmony_ci  if (zep->type != 1) {
141195972f6Sopenharmony_ci    goto err_return;
142195972f6Sopenharmony_ci  }
143195972f6Sopenharmony_ci  if (zep->crc_mode != 1) {
144195972f6Sopenharmony_ci    goto err_return;
145195972f6Sopenharmony_ci  }
146195972f6Sopenharmony_ci  if (zep->len != p->tot_len - sizeof(struct zep_hdr)) {
147195972f6Sopenharmony_ci    goto err_return;
148195972f6Sopenharmony_ci  }
149195972f6Sopenharmony_ci  /* everything seems to be OK, hide the ZEP header */
150195972f6Sopenharmony_ci  if (pbuf_remove_header(p, sizeof(struct zep_hdr))) {
151195972f6Sopenharmony_ci    goto err_return;
152195972f6Sopenharmony_ci  }
153195972f6Sopenharmony_ci  /* TODO Check CRC? */
154195972f6Sopenharmony_ci  /* remove CRC trailer */
155195972f6Sopenharmony_ci  pbuf_realloc(p, p->tot_len - 2);
156195972f6Sopenharmony_ci
157195972f6Sopenharmony_ci  /* Call into 6LoWPAN code. */
158195972f6Sopenharmony_ci  err = netif_lowpan6->input(p, netif_lowpan6);
159195972f6Sopenharmony_ci  if (err == ERR_OK) {
160195972f6Sopenharmony_ci    return;
161195972f6Sopenharmony_ci  }
162195972f6Sopenharmony_cierr_return:
163195972f6Sopenharmony_ci  pbuf_free(p);
164195972f6Sopenharmony_ci}
165195972f6Sopenharmony_ci
166195972f6Sopenharmony_ci/* Send 6LoWPAN TX packets as UDP broadcast */
167195972f6Sopenharmony_cistatic err_t
168195972f6Sopenharmony_cizepif_linkoutput(struct netif *netif, struct pbuf *p)
169195972f6Sopenharmony_ci{
170195972f6Sopenharmony_ci  err_t err;
171195972f6Sopenharmony_ci  struct pbuf *q;
172195972f6Sopenharmony_ci  struct zep_hdr *zep;
173195972f6Sopenharmony_ci  struct zepif_state *state;
174195972f6Sopenharmony_ci
175195972f6Sopenharmony_ci  LWIP_ASSERT("invalid netif", netif != NULL);
176195972f6Sopenharmony_ci  LWIP_ASSERT("invalid pbuf", p != NULL);
177195972f6Sopenharmony_ci
178195972f6Sopenharmony_ci  if (p->tot_len > ZEP_MAX_DATA_LEN) {
179195972f6Sopenharmony_ci    return ERR_VAL;
180195972f6Sopenharmony_ci  }
181195972f6Sopenharmony_ci  LWIP_ASSERT("TODO: support chained pbufs", p->next == NULL);
182195972f6Sopenharmony_ci
183195972f6Sopenharmony_ci  state = (struct zepif_state *)netif->state;
184195972f6Sopenharmony_ci  LWIP_ASSERT("state->pcb != NULL", state->pcb != NULL);
185195972f6Sopenharmony_ci
186195972f6Sopenharmony_ci  q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct zep_hdr) + p->tot_len, PBUF_RAM);
187195972f6Sopenharmony_ci  if (q == NULL) {
188195972f6Sopenharmony_ci    return ERR_MEM;
189195972f6Sopenharmony_ci  }
190195972f6Sopenharmony_ci  zep = (struct zep_hdr *)q->payload;
191195972f6Sopenharmony_ci  memset(zep, 0, sizeof(struct zep_hdr));
192195972f6Sopenharmony_ci  zep->prot_id[0] = 'E';
193195972f6Sopenharmony_ci  zep->prot_id[1] = 'X';
194195972f6Sopenharmony_ci  zep->prot_version = 2;
195195972f6Sopenharmony_ci  zep->type = 1; /* Data */
196195972f6Sopenharmony_ci  zep->channel_id = 0; /* whatever */
197195972f6Sopenharmony_ci  zep->device_id = lwip_htons(1); /* whatever */
198195972f6Sopenharmony_ci  zep->crc_mode = 1;
199195972f6Sopenharmony_ci  zep->unknown_1 = 0xff;
200195972f6Sopenharmony_ci  zep->seq_num = lwip_htonl(state->seqno);
201195972f6Sopenharmony_ci  state->seqno++;
202195972f6Sopenharmony_ci  zep->len = (u8_t)p->tot_len;
203195972f6Sopenharmony_ci
204195972f6Sopenharmony_ci  err = pbuf_copy_partial_pbuf(q, p, p->tot_len, sizeof(struct zep_hdr));
205195972f6Sopenharmony_ci  if (err == ERR_OK) {
206195972f6Sopenharmony_ci#if ZEPIF_LOOPBACK
207195972f6Sopenharmony_ci    zepif_udp_recv(netif, state->pcb, pbuf_clone(PBUF_RAW, PBUF_RAM, q), NULL, 0);
208195972f6Sopenharmony_ci#endif
209195972f6Sopenharmony_ci    err = udp_sendto(state->pcb, q, state->init.zep_dst_ip_addr, state->init.zep_dst_udp_port);
210195972f6Sopenharmony_ci  }
211195972f6Sopenharmony_ci  pbuf_free(q);
212195972f6Sopenharmony_ci
213195972f6Sopenharmony_ci  return err;
214195972f6Sopenharmony_ci}
215195972f6Sopenharmony_ci
216195972f6Sopenharmony_ci/**
217195972f6Sopenharmony_ci * @ingroup zepif
218195972f6Sopenharmony_ci * Set up a raw 6LowPAN netif and surround it with input- and output
219195972f6Sopenharmony_ci * functions for ZEP
220195972f6Sopenharmony_ci */
221195972f6Sopenharmony_cierr_t
222195972f6Sopenharmony_cizepif_init(struct netif *netif)
223195972f6Sopenharmony_ci{
224195972f6Sopenharmony_ci  err_t err;
225195972f6Sopenharmony_ci  struct zepif_init *init_state = (struct zepif_init *)netif->state;
226195972f6Sopenharmony_ci  struct zepif_state *state = (struct zepif_state *)mem_malloc(sizeof(struct zepif_state));
227195972f6Sopenharmony_ci
228195972f6Sopenharmony_ci  LWIP_ASSERT("zepif needs an input callback", netif->input != NULL);
229195972f6Sopenharmony_ci
230195972f6Sopenharmony_ci  if (state == NULL) {
231195972f6Sopenharmony_ci    return ERR_MEM;
232195972f6Sopenharmony_ci  }
233195972f6Sopenharmony_ci  memset(state, 0, sizeof(struct zepif_state));
234195972f6Sopenharmony_ci  if (init_state != NULL) {
235195972f6Sopenharmony_ci    memcpy(&state->init, init_state, sizeof(struct zepif_init));
236195972f6Sopenharmony_ci  }
237195972f6Sopenharmony_ci  if (state->init.zep_src_udp_port == 0) {
238195972f6Sopenharmony_ci    state->init.zep_src_udp_port = ZEPIF_DEFAULT_UDP_PORT;
239195972f6Sopenharmony_ci  }
240195972f6Sopenharmony_ci  if (state->init.zep_dst_udp_port == 0) {
241195972f6Sopenharmony_ci    state->init.zep_dst_udp_port = ZEPIF_DEFAULT_UDP_PORT;
242195972f6Sopenharmony_ci  }
243195972f6Sopenharmony_ci#if LWIP_IPV4
244195972f6Sopenharmony_ci  if (state->init.zep_dst_ip_addr == NULL) {
245195972f6Sopenharmony_ci    /* With IPv4 enabled, default to broadcasting packets if no address is set */
246195972f6Sopenharmony_ci    state->init.zep_dst_ip_addr = IP_ADDR_BROADCAST;
247195972f6Sopenharmony_ci  }
248195972f6Sopenharmony_ci#endif /* LWIP_IPV4 */
249195972f6Sopenharmony_ci
250195972f6Sopenharmony_ci  netif->state = NULL;
251195972f6Sopenharmony_ci
252195972f6Sopenharmony_ci  state->pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
253195972f6Sopenharmony_ci  if (state->pcb == NULL) {
254195972f6Sopenharmony_ci    err = ERR_MEM;
255195972f6Sopenharmony_ci    goto err_ret;
256195972f6Sopenharmony_ci  }
257195972f6Sopenharmony_ci  err = udp_bind(state->pcb, state->init.zep_src_ip_addr, state->init.zep_src_udp_port);
258195972f6Sopenharmony_ci  if (err != ERR_OK) {
259195972f6Sopenharmony_ci    goto err_ret;
260195972f6Sopenharmony_ci  }
261195972f6Sopenharmony_ci  if (state->init.zep_netif != NULL) {
262195972f6Sopenharmony_ci    udp_bind_netif(state->pcb, state->init.zep_netif);
263195972f6Sopenharmony_ci  }
264195972f6Sopenharmony_ci  LWIP_ASSERT("udp_bind(lowpan6_broadcast_pcb) failed", err == ERR_OK);
265195972f6Sopenharmony_ci  ip_set_option(state->pcb, SOF_BROADCAST);
266195972f6Sopenharmony_ci  udp_recv(state->pcb, zepif_udp_recv, netif);
267195972f6Sopenharmony_ci
268195972f6Sopenharmony_ci  err = lowpan6_if_init(netif);
269195972f6Sopenharmony_ci  LWIP_ASSERT("lowpan6_if_init set a state", netif->state == NULL);
270195972f6Sopenharmony_ci  if (err == ERR_OK) {
271195972f6Sopenharmony_ci    netif->state = state;
272195972f6Sopenharmony_ci    netif->hwaddr_len = 6;
273195972f6Sopenharmony_ci    if (init_state != NULL) {
274195972f6Sopenharmony_ci      memcpy(netif->hwaddr, init_state->addr, 6);
275195972f6Sopenharmony_ci    } else {
276195972f6Sopenharmony_ci      u8_t i;
277195972f6Sopenharmony_ci      for (i = 0; i < 6; i++) {
278195972f6Sopenharmony_ci        netif->hwaddr[i] = i;
279195972f6Sopenharmony_ci      }
280195972f6Sopenharmony_ci      netif->hwaddr[0] &= 0xfc;
281195972f6Sopenharmony_ci    }
282195972f6Sopenharmony_ci    netif->linkoutput = zepif_linkoutput;
283195972f6Sopenharmony_ci
284195972f6Sopenharmony_ci    if (!zep_lowpan_timer_running) {
285195972f6Sopenharmony_ci      sys_timeout(LOWPAN6_TMR_INTERVAL, zep_lowpan_timer, NULL);
286195972f6Sopenharmony_ci      zep_lowpan_timer_running = 1;
287195972f6Sopenharmony_ci    }
288195972f6Sopenharmony_ci
289195972f6Sopenharmony_ci    return ERR_OK;
290195972f6Sopenharmony_ci  }
291195972f6Sopenharmony_ci
292195972f6Sopenharmony_cierr_ret:
293195972f6Sopenharmony_ci  if (state->pcb != NULL) {
294195972f6Sopenharmony_ci    udp_remove(state->pcb);
295195972f6Sopenharmony_ci  }
296195972f6Sopenharmony_ci  mem_free(state);
297195972f6Sopenharmony_ci  return err;
298195972f6Sopenharmony_ci}
299195972f6Sopenharmony_ci
300195972f6Sopenharmony_ci#endif /* LWIP_IPV6 && LWIP_UDP */
301