Coverage Report

Created: 2024-09-08 06:39

/src/tinyusb/lib/networking/dhserver.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * The MIT License (MIT)
3
 *
4
 * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24
25
#include "dhserver.h"
26
27
/* DHCP message type */
28
0
#define DHCP_DISCOVER       1
29
0
#define DHCP_OFFER          2
30
0
#define DHCP_REQUEST        3
31
#define DHCP_DECLINE        4
32
0
#define DHCP_ACK            5
33
#define DHCP_NAK            6
34
#define DHCP_RELEASE        7
35
#define DHCP_INFORM         8
36
37
/* DHCP options */
38
enum DHCP_OPTIONS
39
{
40
  DHCP_PAD                    = 0,
41
  DHCP_SUBNETMASK             = 1,
42
  DHCP_ROUTER                 = 3,
43
  DHCP_DNSSERVER              = 6,
44
  DHCP_HOSTNAME               = 12,
45
  DHCP_DNSDOMAIN              = 15,
46
  DHCP_MTU                    = 26,
47
  DHCP_BROADCAST              = 28,
48
  DHCP_PERFORMROUTERDISC      = 31,
49
  DHCP_STATICROUTE            = 33,
50
  DHCP_NISDOMAIN              = 40,
51
  DHCP_NISSERVER              = 41,
52
  DHCP_NTPSERVER              = 42,
53
  DHCP_VENDOR                 = 43,
54
  DHCP_IPADDRESS              = 50,
55
  DHCP_LEASETIME              = 51,
56
  DHCP_OPTIONSOVERLOADED      = 52,
57
  DHCP_MESSAGETYPE            = 53,
58
  DHCP_SERVERID               = 54,
59
  DHCP_PARAMETERREQUESTLIST   = 55,
60
  DHCP_MESSAGE                = 56,
61
  DHCP_MAXMESSAGESIZE         = 57,
62
  DHCP_RENEWALTIME            = 58,
63
  DHCP_REBINDTIME             = 59,
64
  DHCP_CLASSID                = 60,
65
  DHCP_CLIENTID               = 61,
66
  DHCP_USERCLASS              = 77,  /* RFC 3004 */
67
  DHCP_FQDN                   = 81,
68
  DHCP_DNSSEARCH              = 119, /* RFC 3397 */
69
  DHCP_CSR                    = 121, /* RFC 3442 */
70
  DHCP_MSCSR                  = 249, /* MS code for RFC 3442 */
71
  DHCP_END                    = 255
72
};
73
74
typedef struct
75
{
76
    uint8_t  dp_op;           /* packet opcode type */
77
    uint8_t  dp_htype;        /* hardware addr type */
78
    uint8_t  dp_hlen;         /* hardware addr length */
79
    uint8_t  dp_hops;         /* gateway hops */
80
    uint32_t dp_xid;          /* transaction ID */
81
    uint16_t dp_secs;         /* seconds since boot began */
82
    uint16_t dp_flags;
83
    uint8_t  dp_ciaddr[4];    /* client IP address */
84
    uint8_t  dp_yiaddr[4];    /* 'your' IP address */
85
    uint8_t  dp_siaddr[4];    /* server IP address */
86
    uint8_t  dp_giaddr[4];    /* gateway IP address */
87
    uint8_t  dp_chaddr[16];   /* client hardware address */
88
    uint8_t  dp_legacy[192];
89
    uint8_t  dp_magic[4];
90
    uint8_t  dp_options[275]; /* options area */
91
} DHCP_TYPE;
92
93
DHCP_TYPE dhcp_data;
94
static struct udp_pcb *pcb = NULL;
95
static const dhcp_config_t *config = NULL;
96
97
char magic_cookie[] = {0x63,0x82,0x53,0x63};
98
99
static ip4_addr_t get_ip(const uint8_t *pnt)
100
0
{
101
0
  ip4_addr_t result;
102
0
  memcpy(&result, pnt, sizeof(result));
103
0
  return result;
104
0
}
105
106
static void set_ip(uint8_t *pnt, ip4_addr_t value)
107
0
{
108
0
  memcpy(pnt, &value.addr, sizeof(value.addr));
109
0
}
110
111
static dhcp_entry_t *entry_by_ip(ip4_addr_t ip)
112
0
{
113
0
  int i;
114
0
  for (i = 0; i < config->num_entry; i++)
115
0
    if (config->entries[i].addr.addr == ip.addr)
116
0
      return &config->entries[i];
117
0
  return NULL;
118
0
}
119
120
static dhcp_entry_t *entry_by_mac(uint8_t *mac)
121
0
{
122
0
  int i;
123
0
  for (i = 0; i < config->num_entry; i++)
124
0
    if (memcmp(config->entries[i].mac, mac, 6) == 0)
125
0
      return &config->entries[i];
126
0
  return NULL;
127
0
}
128
129
static __inline bool is_vacant(dhcp_entry_t *entry)
130
0
{
131
0
  return memcmp("\0\0\0\0\0", entry->mac, 6) == 0;
132
0
}
133
134
static dhcp_entry_t *vacant_address(void)
135
0
{
136
0
  int i;
137
0
  for (i = 0; i < config->num_entry; i++)
138
0
    if (is_vacant(config->entries + i))
139
0
      return config->entries + i;
140
0
  return NULL;
141
0
}
142
143
static __inline void free_entry(dhcp_entry_t *entry)
144
0
{
145
0
  memset(entry->mac, 0, 6);
146
0
}
147
148
uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr)
149
0
{
150
0
  int i = 0;
151
0
  while ((i + 1) < size)
152
0
  {
153
0
    int next = i + attrs[i + 1] + 2;
154
0
    if (next > size) return NULL;
155
0
    if (attrs[i] == attr)
156
0
      return attrs + i;
157
0
    i = next;
158
0
  }
159
0
  return NULL;
160
0
}
161
162
int fill_options(void *dest,
163
  uint8_t msg_type,
164
  const char *domain,
165
  ip4_addr_t dns,
166
  int lease_time,
167
  ip4_addr_t serverid,
168
  ip4_addr_t router,
169
  ip4_addr_t subnet)
170
0
{
171
0
  uint8_t *ptr = (uint8_t *)dest;
172
  /* ACK message type */
173
0
  *ptr++ = 53;
174
0
  *ptr++ = 1;
175
0
  *ptr++ = msg_type;
176
177
  /* dhcp server identifier */
178
0
  *ptr++ = DHCP_SERVERID;
179
0
  *ptr++ = 4;
180
0
  set_ip(ptr, serverid);
181
0
  ptr += 4;
182
183
  /* lease time */
184
0
  *ptr++ = DHCP_LEASETIME;
185
0
  *ptr++ = 4;
186
0
  *ptr++ = (lease_time >> 24) & 0xFF;
187
0
  *ptr++ = (lease_time >> 16) & 0xFF;
188
0
  *ptr++ = (lease_time >> 8) & 0xFF;
189
0
  *ptr++ = (lease_time >> 0) & 0xFF;
190
191
  /* subnet mask */
192
0
  *ptr++ = DHCP_SUBNETMASK;
193
0
  *ptr++ = 4;
194
0
  set_ip(ptr, subnet);
195
0
  ptr += 4;
196
197
  /* router */
198
0
  if (router.addr != 0)
199
0
  {
200
0
    *ptr++ = DHCP_ROUTER;
201
0
    *ptr++ = 4;
202
0
    set_ip(ptr, router);
203
0
    ptr += 4;
204
0
  }
205
206
  /* domain name */
207
0
  if (domain != NULL)
208
0
  {
209
0
    int len = strlen(domain);
210
0
    *ptr++ = DHCP_DNSDOMAIN;
211
0
    *ptr++ = len;
212
0
    memcpy(ptr, domain, len);
213
0
    ptr += len;
214
0
  }
215
216
  /* domain name server (DNS) */
217
0
  if (dns.addr != 0)
218
0
  {
219
0
    *ptr++ = DHCP_DNSSERVER;
220
0
    *ptr++ = 4;
221
0
    set_ip(ptr, dns);
222
0
    ptr += 4;
223
0
  }
224
225
  /* end */
226
0
  *ptr++ = DHCP_END;
227
0
  return ptr - (uint8_t *)dest;
228
0
}
229
230
static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
231
0
{
232
0
  uint8_t *ptr;
233
0
  dhcp_entry_t *entry;
234
0
  struct pbuf *pp;
235
0
  struct netif *netif = netif_get_by_index(p->if_idx);
236
237
0
  (void)arg;
238
0
  (void)addr;
239
240
0
  unsigned n = p->len;
241
0
  if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data);
242
0
  memcpy(&dhcp_data, p->payload, n);
243
244
0
  ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_MESSAGETYPE);
245
0
  if (ptr == NULL)
246
0
  {
247
0
    pbuf_free(p);
248
0
    return;
249
0
  }
250
251
0
  switch (ptr[2])
252
0
  {
253
0
    case DHCP_DISCOVER:
254
0
      entry = entry_by_mac(dhcp_data.dp_chaddr);
255
0
      if (entry == NULL) entry = vacant_address();
256
0
      if (entry == NULL) break;
257
258
0
      dhcp_data.dp_op = 2; /* reply */
259
0
      dhcp_data.dp_secs = 0;
260
0
      dhcp_data.dp_flags = 0;
261
0
      set_ip(dhcp_data.dp_yiaddr, entry->addr);
262
0
      memcpy(dhcp_data.dp_magic, magic_cookie, 4);
263
264
0
      memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
265
266
0
      fill_options(dhcp_data.dp_options,
267
0
        DHCP_OFFER,
268
0
        config->domain,
269
0
        config->dns,
270
0
        entry->lease,
271
0
        *netif_ip4_addr(netif),
272
0
        config->router,
273
0
        *netif_ip4_netmask(netif));
274
275
0
      pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
276
0
      if (pp == NULL) break;
277
0
      memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
278
0
      udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
279
0
      pbuf_free(pp);
280
0
      break;
281
282
0
    case DHCP_REQUEST:
283
      /* 1. find requested ipaddr in option list */
284
0
      ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS);
285
0
      if (ptr == NULL) break;
286
0
      if (ptr[1] != 4) break;
287
0
      ptr += 2;
288
289
      /* 2. does hw-address registered? */
290
0
      entry = entry_by_mac(dhcp_data.dp_chaddr);
291
0
      if (entry != NULL) free_entry(entry);
292
293
      /* 3. find requested ipaddr */
294
0
      entry = entry_by_ip(get_ip(ptr));
295
0
      if (entry == NULL) break;
296
0
      if (!is_vacant(entry)) break;
297
298
      /* 4. fill struct fields */
299
0
      memcpy(dhcp_data.dp_yiaddr, ptr, 4);
300
0
      dhcp_data.dp_op = 2; /* reply */
301
0
      dhcp_data.dp_secs = 0;
302
0
      dhcp_data.dp_flags = 0;
303
0
      memcpy(dhcp_data.dp_magic, magic_cookie, 4);
304
305
      /* 5. fill options */
306
0
      memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
307
308
0
      fill_options(dhcp_data.dp_options,
309
0
        DHCP_ACK,
310
0
        config->domain,
311
0
        config->dns,
312
0
        entry->lease,
313
0
        *netif_ip4_addr(netif),
314
0
        config->router,
315
0
        *netif_ip4_netmask(netif));
316
317
      /* 6. send ACK */
318
0
      pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
319
0
      if (pp == NULL) break;
320
0
      memcpy(entry->mac, dhcp_data.dp_chaddr, 6);
321
0
      memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
322
0
      udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
323
0
      pbuf_free(pp);
324
0
      break;
325
326
0
    default:
327
0
        break;
328
0
  }
329
0
  pbuf_free(p);
330
0
}
331
332
err_t dhserv_init(const dhcp_config_t *c)
333
0
{
334
0
  err_t err;
335
0
  udp_init();
336
0
  dhserv_free();
337
0
  pcb = udp_new();
338
0
  if (pcb == NULL)
339
0
    return ERR_MEM;
340
0
  err = udp_bind(pcb, IP_ADDR_ANY, c->port);
341
0
  if (err != ERR_OK)
342
0
  {
343
0
    dhserv_free();
344
0
    return err;
345
0
  }
346
0
  udp_recv(pcb, udp_recv_proc, NULL);
347
0
  config = c;
348
0
  return ERR_OK;
349
0
}
350
351
void dhserv_free(void)
352
0
{
353
0
  if (pcb == NULL) return;
354
0
  udp_remove(pcb);
355
0
  pcb = NULL;
356
0
}