Blame view

RIOT/sys/net/gnrc/transport_layer/udp/gnrc_udp.c 8.69 KB
a752c7ab   elopes   add first test an...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
  /*
   * 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     net_gnrc_udp
   * @{
   *
   * @file
   * @brief       UDP implementation
   *
   * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
   * @}
   */
  
  #include <stdint.h>
  #include <errno.h>
  
  #include "byteorder.h"
  #include "msg.h"
  #include "thread.h"
  #include "utlist.h"
  #include "net/ipv6/hdr.h"
  #include "net/gnrc/udp.h"
  #include "net/gnrc.h"
  #include "net/inet_csum.h"
  
  
  #define ENABLE_DEBUG    (0)
  #include "debug.h"
  
  /**
   * @brief   Save the UDP's thread PID for later reference
   */
  static kernel_pid_t _pid = KERNEL_PID_UNDEF;
  
  /**
   * @brief   Allocate memory for the UDP thread's stack
   */
  #if ENABLE_DEBUG
  static char _stack[GNRC_UDP_STACK_SIZE + THREAD_EXTRA_STACKSIZE_PRINTF];
  #else
  static char _stack[GNRC_UDP_STACK_SIZE];
  #endif
  
  /**
   * @brief   Calculate the UDP checksum dependent on the network protocol
   *
   * @note    If the checksum turns out to be 0x0000, the function returns 0xffff
   *          as specified in RFC768
   *
   * @param[in] pkt           pointer to the packet in the packet buffer
   * @param[in] pseudo_hdr    pointer to the network layer header
   * @param[in] payload       pointer to the payload
   *
   * @return                  the checksum of the pkt in host byte order
   * @return                  0 on error
   */
  static uint16_t _calc_csum(gnrc_pktsnip_t *hdr, gnrc_pktsnip_t *pseudo_hdr,
                             gnrc_pktsnip_t *payload)
  {
      uint16_t csum = 0;
      uint16_t len = (uint16_t)hdr->size;
  
      /* process the payload */
      while (payload && payload != hdr && payload != pseudo_hdr) {
          csum = inet_csum_slice(csum, (uint8_t *)(payload->data), payload->size, len);
          len += (uint16_t)payload->size;
          payload = payload->next;
      }
      /* process applicable UDP header bytes */
      csum = inet_csum(csum, (uint8_t *)hdr->data, sizeof(udp_hdr_t));
  
      switch (pseudo_hdr->type) {
  #ifdef MODULE_GNRC_IPV6
          case GNRC_NETTYPE_IPV6:
              csum = ipv6_hdr_inet_csum(csum, pseudo_hdr->data, PROTNUM_UDP, len);
              break;
  #endif
          default:
              (void)len;
              return 0;
      }
      /* return inverted results */
      if (csum == 0xFFFF) {
          /* https://tools.ietf.org/html/rfc2460#section-8.1
           * bullet 4
           * "if that computation yields a result of zero, it must be changed
           * to hex FFFF for placement in the UDP header."
           */
          return 0xFFFF;
      } else {
          return ~csum;
      }
  }
  
  static void _receive(gnrc_pktsnip_t *pkt)
  {
      gnrc_pktsnip_t *udp, *ipv6;
      udp_hdr_t *hdr;
      uint32_t port;
  
      /* mark UDP header */
      udp = gnrc_pktbuf_start_write(pkt);
      if (udp == NULL) {
          DEBUG("udp: unable to get write access to packet\n");
          gnrc_pktbuf_release(pkt);
          return;
      }
      pkt = udp;
  
      ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
  
      assert(ipv6 != NULL);
  
      if ((pkt->next != NULL) && (pkt->next->type == GNRC_NETTYPE_UDP) &&
          (pkt->next->size == sizeof(udp_hdr_t))) {
          /* UDP header was already marked. Take it. */
          udp = pkt->next;
      }
      else {
          udp = gnrc_pktbuf_mark(pkt, sizeof(udp_hdr_t), GNRC_NETTYPE_UDP);
          if (udp == NULL) {
              DEBUG("udp: error marking UDP header, dropping packet\n");
              gnrc_pktbuf_release(pkt);
              return;
          }
      }
      /* mark payload as Type: UNDEF */
      pkt->type = GNRC_NETTYPE_UNDEF;
      /* get explicit pointer to UDP header */
      hdr = (udp_hdr_t *)udp->data;
  
      /* validate checksum */
      if (byteorder_ntohs(hdr->checksum) == 0) {
          /* RFC 2460 Section 8.1
           * "IPv6 receivers must discard UDP packets containing a zero checksum,
           * and should log the error."
           */
          DEBUG("udp: received packet with zero checksum, dropping it\n");
          gnrc_pktbuf_release(pkt);
          return;
      }
      if (_calc_csum(udp, ipv6, pkt) != 0xFFFF) {
          DEBUG("udp: received packet with invalid checksum, dropping it\n");
          gnrc_pktbuf_release(pkt);
          return;
      }
  
      /* get port (netreg demux context) */
      port = (uint32_t)byteorder_ntohs(hdr->dst_port);
  
      /* send payload to receivers */
      if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_UDP, port, pkt)) {
          DEBUG("udp: unable to forward packet as no one is interested in it\n");
          gnrc_pktbuf_release(pkt);
      }
  }
  
  static void _send(gnrc_pktsnip_t *pkt)
  {
      udp_hdr_t *hdr;
      gnrc_pktsnip_t *udp_snip, *tmp;
      gnrc_nettype_t target_type = pkt->type;
  
      /* write protect first header */
      tmp = gnrc_pktbuf_start_write(pkt);
      if (tmp == NULL) {
          DEBUG("udp: cannot send packet: unable to allocate packet\n");
          gnrc_pktbuf_release(pkt);
          return;
      }
      pkt = tmp;
      udp_snip = tmp->next;
  
      /* get and write protect until udp snip */
      while ((udp_snip != NULL) && (udp_snip->type != GNRC_NETTYPE_UDP)) {
          udp_snip = gnrc_pktbuf_start_write(udp_snip);
          if (udp_snip == NULL) {
              DEBUG("udp: cannot send packet: unable to allocate packet\n");
              gnrc_pktbuf_release(pkt);
              return;
          }
          tmp->next = udp_snip;
          tmp = udp_snip;
          udp_snip = udp_snip->next;
      }
  
      assert(udp_snip != NULL);
  
      /* write protect UDP snip */
      udp_snip = gnrc_pktbuf_start_write(udp_snip);
      if (udp_snip == NULL) {
          DEBUG("udp: cannot send packet: unable to allocate packet\n");
          gnrc_pktbuf_release(pkt);
          return;
      }
      tmp->next = udp_snip;
      hdr = (udp_hdr_t *)udp_snip->data;
      /* fill in size field */
      hdr->length = byteorder_htons(gnrc_pkt_len(udp_snip));
  
      /* set to IPv6, if first header is netif header */
      if (target_type == GNRC_NETTYPE_NETIF) {
          target_type = pkt->next->type;
      }
  
      /* and forward packet to the network layer */
      if (!gnrc_netapi_dispatch_send(target_type, GNRC_NETREG_DEMUX_CTX_ALL,
                                     pkt)) {
          DEBUG("udp: cannot send packet: network layer not found\n");
          gnrc_pktbuf_release(pkt);
      }
  }
  
  static void *_event_loop(void *arg)
  {
      (void)arg;
      msg_t msg, reply;
      msg_t msg_queue[GNRC_UDP_MSG_QUEUE_SIZE];
      gnrc_netreg_entry_t netreg = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL,
                                                              sched_active_pid);
      /* preset reply message */
      reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
      reply.content.value = (uint32_t)-ENOTSUP;
      /* initialize message queue */
      msg_init_queue(msg_queue, GNRC_UDP_MSG_QUEUE_SIZE);
      /* register UPD at netreg */
      gnrc_netreg_register(GNRC_NETTYPE_UDP, &netreg);
  
      /* dispatch NETAPI messages */
      while (1) {
          msg_receive(&msg);
          switch (msg.type) {
              case GNRC_NETAPI_MSG_TYPE_RCV:
                  DEBUG("udp: GNRC_NETAPI_MSG_TYPE_RCV\n");
                  _receive(msg.content.ptr);
                  break;
              case GNRC_NETAPI_MSG_TYPE_SND:
                  DEBUG("udp: GNRC_NETAPI_MSG_TYPE_SND\n");
                  _send(msg.content.ptr);
                  break;
              case GNRC_NETAPI_MSG_TYPE_SET:
              case GNRC_NETAPI_MSG_TYPE_GET:
                  msg_reply(&msg, &reply);
                  break;
              default:
                  DEBUG("udp: received unidentified message\n");
                  break;
          }
      }
  
      /* never reached */
      return NULL;
  }
  
  int gnrc_udp_calc_csum(gnrc_pktsnip_t *hdr, gnrc_pktsnip_t *pseudo_hdr)
  {
      uint16_t csum;
  
      if ((hdr == NULL) || (pseudo_hdr == NULL)) {
          return -EFAULT;
      }
      if (hdr->type != GNRC_NETTYPE_UDP) {
          return -EBADMSG;
      }
  
      csum = _calc_csum(hdr, pseudo_hdr, hdr->next);
      if (csum == 0) {
          return -ENOENT;
      }
      ((udp_hdr_t *)hdr->data)->checksum = byteorder_htons(csum);
      return 0;
  }
  
  gnrc_pktsnip_t *gnrc_udp_hdr_build(gnrc_pktsnip_t *payload, uint16_t src,
                                     uint16_t dst)
  {
      gnrc_pktsnip_t *res;
      udp_hdr_t *hdr;
  
      /* allocate header */
      res = gnrc_pktbuf_add(payload, NULL, sizeof(udp_hdr_t), GNRC_NETTYPE_UDP);
      if (res == NULL) {
          return NULL;
      }
      /* initialize header */
      hdr = (udp_hdr_t *)res->data;
      hdr->src_port = byteorder_htons(src);
      hdr->dst_port = byteorder_htons(dst);
      hdr->checksum = byteorder_htons(0);
      return res;
  }
  
  int gnrc_udp_init(void)
  {
      /* check if thread is already running */
      if (_pid == KERNEL_PID_UNDEF) {
          /* start UDP thread */
          _pid = thread_create(_stack, sizeof(_stack), GNRC_UDP_PRIO,
                               THREAD_CREATE_STACKTEST, _event_loop, NULL, "udp");
      }
      return _pid;
  }