Coverage Report

Created: 2025-08-29 06:42

/src/dnsmasq/src/dhcp-common.c
Line
Count
Source (jump to first uncovered line)
1
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 dated June, 1991, or
6
   (at your option) version 3 dated 29 June, 2007.
7
 
8
   This program is distributed in the hope that it will be useful,
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
   GNU General Public License for more details.
12
     
13
   You should have received a copy of the GNU General Public License
14
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
*/
16
17
#include "dnsmasq.h"
18
19
#ifdef HAVE_DHCP
20
21
void dhcp_common_init(void)
22
0
{
23
  /* These each hold a DHCP option max size 255
24
     and get a terminating zero added */
25
0
  daemon->dhcp_buff = safe_malloc(DHCP_BUFF_SZ);
26
0
  daemon->dhcp_buff2 = safe_malloc(DHCP_BUFF_SZ); 
27
0
  daemon->dhcp_buff3 = safe_malloc(DHCP_BUFF_SZ);
28
  
29
  /* dhcp_packet is used by v4 and v6, outpacket only by v6 
30
     sizeof(struct dhcp_packet) is as good an initial size as any,
31
     even for v6 */
32
0
  expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet));
33
0
#ifdef HAVE_DHCP6
34
0
  if (daemon->dhcp6)
35
0
    expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
36
0
#endif
37
0
}
38
39
ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
40
0
{  
41
0
  ssize_t sz, new_sz;
42
 
43
0
  while (1)
44
0
    {
45
0
      msg->msg_flags = 0;
46
0
      while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
47
      
48
0
      if (sz == -1)
49
0
  return -1;
50
      
51
0
      if (!(msg->msg_flags & MSG_TRUNC))
52
0
  break;
53
54
      /* Very new Linux kernels return the actual size needed, 
55
   older ones always return truncated size */
56
0
      if ((size_t)sz == msg->msg_iov->iov_len)
57
0
  {
58
0
    if (!expand_buf(msg->msg_iov, sz + 100))
59
0
      return -1;
60
0
  }
61
0
      else
62
0
  {
63
0
    expand_buf(msg->msg_iov, sz);
64
0
    break;
65
0
  }
66
0
    }
67
  
68
0
  while ((new_sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR);
69
70
  /* Some kernels seem to ignore MSG_PEEK, and dequeue the packet anyway. 
71
     If that happens we get EAGAIN here because the socket is non-blocking.
72
     Use the result of the original testing recvmsg as long as the buffer
73
     was big enough. There's a small race here that may lose the odd packet,
74
     but it's UDP anyway. */
75
  
76
0
  if (new_sz == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
77
0
    new_sz = sz;
78
  
79
0
  return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
80
0
}
81
82
/* like match_netid() except that the check can have a trailing * for wildcard */
83
/* started as a direct copy of match_netid() */
84
int match_netid_wild(struct dhcp_netid *check, struct dhcp_netid *pool)
85
0
{
86
0
  struct dhcp_netid *tmp1;
87
  
88
0
  for (; check; check = check->next)
89
0
    {
90
0
      const int check_len = strlen(check->net);
91
0
      const int is_wc = (check_len > 0 && check->net[check_len - 1] == '*');
92
      
93
      /* '#' for not is for backwards compat. */
94
0
      if (check->net[0] != '!' && check->net[0] != '#')
95
0
  {
96
0
    for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
97
0
      if (is_wc ? (strncmp(check->net, tmp1->net, check_len-1) == 0) :
98
0
    (strcmp(check->net, tmp1->net) == 0))
99
0
        break;
100
0
    if (!tmp1)
101
0
      return 0;
102
0
  }
103
0
      else
104
0
  for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
105
0
    if (is_wc ? (strncmp((check->net)+1, tmp1->net, check_len-2) == 0) :
106
0
        (strcmp((check->net)+1, tmp1->net) == 0))
107
0
      return 0;
108
0
    }
109
0
  return 1;
110
0
}
111
112
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
113
0
{
114
0
  struct tag_if *exprs;
115
0
  struct dhcp_netid_list *list;
116
117
  /* this now uses match_netid_wild() above so that tag_if can
118
   * be used to set a 'group of interfaces' tag.
119
   */
120
0
  for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
121
0
    if (match_netid_wild(exprs->tag, tags))
122
0
      for (list = exprs->set; list; list = list->next)
123
0
  {
124
0
    list->list->next = tags;
125
0
    tags = list->list;
126
0
  }
127
128
0
  return tags;
129
0
}
130
131
/* pxemode == 0 -> don't include dhcp-option-pxe options.
132
   pxemode == 1 -> do include dhcp-option-pxe options.
133
   pxemode == 2 -> include ONLY dhcp-option-pxe options. */
134
int pxe_ok(struct dhcp_opt *opt, int pxemode)
135
0
{
136
0
  if (opt->flags & DHOPT_PXE_OPT)
137
0
    {
138
0
      if (pxemode != 0)
139
0
  return 1;
140
0
    }
141
0
  else
142
0
    {
143
0
      if (pxemode != 2)
144
0
  return 1;
145
0
    }
146
  
147
0
  return 0;
148
0
}
149
150
struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts, int pxemode)
151
0
{
152
0
  struct dhcp_netid *tagif = run_tag_if(tags);
153
0
  struct dhcp_opt *opt;
154
0
  struct dhcp_opt *tmp;  
155
  
156
  /* flag options which are valid with the current tag set (sans context tags) */
157
0
  for (opt = opts; opt; opt = opt->next)
158
0
    {
159
0
      opt->flags &= ~DHOPT_TAGOK;
160
0
      if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
161
0
    match_netid(opt->netid, tagif, 0) &&
162
0
    pxe_ok(opt, pxemode))
163
0
  opt->flags |= DHOPT_TAGOK;
164
0
    }
165
  
166
  /* now flag options which are valid, including the context tags,
167
     otherwise valid options are inhibited if we found a higher priority one above */
168
0
  if (context_tags)
169
0
    {
170
0
      struct dhcp_netid *last_tag;
171
172
0
      for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
173
0
      last_tag->next = tags;
174
0
      tagif = run_tag_if(context_tags);
175
      
176
      /* reset stuff with tag:!<tag> which now matches. */
177
0
      for (opt = opts; opt; opt = opt->next)
178
0
  if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
179
0
      (opt->flags & DHOPT_TAGOK) &&
180
0
      !match_netid(opt->netid, tagif, 0))
181
0
    opt->flags &= ~DHOPT_TAGOK;
182
183
0
      for (opt = opts; opt; opt = opt->next)
184
0
  if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
185
0
      match_netid(opt->netid, tagif, 0) &&
186
0
      pxe_ok(opt, pxemode))
187
0
    {
188
0
      struct dhcp_opt *tmp;  
189
0
      for (tmp = opts; tmp; tmp = tmp->next) 
190
0
        if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
191
0
    break;
192
0
      if (!tmp)
193
0
        opt->flags |= DHOPT_TAGOK;
194
0
    }      
195
0
    }
196
  
197
  /* now flag untagged options which are not overridden by tagged ones */
198
0
  for (opt = opts; opt; opt = opt->next)
199
0
    if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
200
0
  !opt->netid &&
201
0
  pxe_ok(opt, pxemode))
202
0
      {
203
0
  for (tmp = opts; tmp; tmp = tmp->next) 
204
0
    if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
205
0
      break;
206
0
  if (!tmp)
207
0
    opt->flags |= DHOPT_TAGOK;
208
0
  else if (!tmp->netid)
209
0
    my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); 
210
0
      }
211
  
212
  /* Finally, eliminate duplicate options later in the chain, and therefore earlier in the config file. */
213
0
  for (opt = opts; opt; opt = opt->next)
214
0
    if (opt->flags & DHOPT_TAGOK)
215
0
      for (tmp = opt->next; tmp; tmp = tmp->next) 
216
0
  if (tmp->opt == opt->opt)
217
0
    tmp->flags &= ~DHOPT_TAGOK;
218
  
219
0
  return tagif;
220
0
}
221
  
222
/* Is every member of check matched by a member of pool? 
223
   If tagnotneeded, untagged is OK */
224
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
225
0
{
226
0
  struct dhcp_netid *tmp1;
227
  
228
0
  if (!check && !tagnotneeded)
229
0
    return 0;
230
231
0
  for (; check; check = check->next)
232
0
    {
233
      /* '#' for not is for backwards compat. */
234
0
      if (check->net[0] != '!' && check->net[0] != '#')
235
0
  {
236
0
    for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
237
0
      if (strcmp(check->net, tmp1->net) == 0)
238
0
        break;
239
0
    if (!tmp1)
240
0
      return 0;
241
0
  }
242
0
      else
243
0
  for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
244
0
    if (strcmp((check->net)+1, tmp1->net) == 0)
245
0
      return 0;
246
0
    }
247
0
  return 1;
248
0
}
249
250
/* return domain or NULL if none. */
251
char *strip_hostname(char *hostname)
252
0
{
253
0
  char *dot = strchr(hostname, '.');
254
 
255
0
  if (!dot)
256
0
    return NULL;
257
  
258
0
  *dot = 0; /* truncate */
259
0
  if (strlen(dot+1) != 0)
260
0
    return dot+1;
261
  
262
0
  return NULL;
263
0
}
264
265
void log_tags(struct dhcp_netid *netid, u32 xid)
266
0
{
267
0
  if (netid && option_bool(OPT_LOG_OPTS))
268
0
    {
269
0
      char *s = daemon->namebuff;
270
0
      for (*s = 0; netid; netid = netid->next)
271
0
  {
272
    /* kill dupes. */
273
0
    struct dhcp_netid *n;
274
    
275
0
    for (n = netid->next; n; n = n->next)
276
0
      if (strcmp(netid->net, n->net) == 0)
277
0
        break;
278
    
279
0
    if (!n)
280
0
      {
281
0
        strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
282
0
        if (netid->next)
283
0
    strncat (s, ", ", (MAXDNAME-1) - strlen(s));
284
0
      }
285
0
  }
286
0
      my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
287
0
    } 
288
0
}   
289
  
290
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
291
0
{
292
0
  int i;
293
  
294
0
  if (o->len > len)
295
0
    return 0;
296
  
297
0
  if (o->len == 0)
298
0
    return 1;
299
     
300
0
  if (o->flags & DHOPT_HEX)
301
0
    { 
302
0
      if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
303
0
  return 1;
304
0
    }
305
0
  else 
306
0
    for (i = 0; i <= (len - o->len); ) 
307
0
      {
308
0
  if (memcmp(o->val, p + i, o->len) == 0)
309
0
    return 1;
310
      
311
0
  if (o->flags & DHOPT_STRING)
312
0
    i++;
313
0
  else
314
0
    i += o->len;
315
0
      }
316
  
317
0
  return 0;
318
0
}
319
320
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
321
0
{
322
0
  struct hwaddr_config *conf_addr;
323
  
324
0
  for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
325
0
    if (conf_addr->wildcard_mask == 0 &&
326
0
  conf_addr->hwaddr_len == len &&
327
0
  (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
328
0
  memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
329
0
      return 1;
330
  
331
0
  return 0;
332
0
}
333
334
static int is_config_in_context(struct dhcp_context *context, struct dhcp_config *config)
335
0
{
336
0
  if (!context) /* called via find_config() from lease_update_from_configs() */
337
0
    return 1; 
338
339
0
  if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
340
0
    return 1;
341
  
342
0
#ifdef HAVE_DHCP6
343
0
  if (context->flags & CONTEXT_V6)
344
0
    {
345
0
       struct addrlist *addr_list;
346
347
0
       if (config->flags & CONFIG_ADDR6)
348
0
   for (; context; context = context->current)
349
0
     for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
350
0
       {
351
0
         if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
352
0
     return 1;
353
         
354
0
         if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
355
0
     return 1;
356
0
       }
357
0
    }
358
0
  else
359
0
#endif
360
0
    {
361
0
      for (; context; context = context->current)
362
0
  if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
363
0
    return 1;
364
0
    }
365
366
0
  return 0;
367
0
}
368
369
static struct dhcp_config *find_config_match(struct dhcp_config *configs,
370
               struct dhcp_context *context,
371
               unsigned char *clid, int clid_len,
372
               unsigned char *hwaddr, int hw_len, 
373
               int hw_type, char *hostname,
374
               struct dhcp_netid *tags, int tag_not_needed)
375
0
{
376
0
  int count, new;
377
0
  struct dhcp_config *config, *candidate; 
378
0
  struct hwaddr_config *conf_addr;
379
380
0
  if (clid)
381
0
    for (config = configs; config; config = config->next)
382
0
      if (config->flags & CONFIG_CLID)
383
0
  {
384
0
    if (config->clid_len == clid_len && 
385
0
        memcmp(config->clid, clid, clid_len) == 0 &&
386
0
        is_config_in_context(context, config) &&
387
0
        match_netid(config->filter, tags, tag_not_needed))
388
      
389
0
      return config;
390
    
391
    /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
392
       cope with that here. This is IPv4 only. context==NULL implies IPv4, 
393
       see lease_update_from_configs() */
394
0
    if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1  &&
395
0
        memcmp(config->clid, clid+1, clid_len-1) == 0 &&
396
0
        is_config_in_context(context, config) &&
397
0
        match_netid(config->filter, tags, tag_not_needed))
398
0
      return config;
399
0
  }
400
  
401
402
0
  if (hwaddr)
403
0
    for (config = configs; config; config = config->next)
404
0
      if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
405
0
    is_config_in_context(context, config) &&
406
0
    match_netid(config->filter, tags, tag_not_needed))
407
0
  return config;
408
  
409
0
  if (hostname && context)
410
0
    for (config = configs; config; config = config->next)
411
0
      if ((config->flags & CONFIG_NAME) && 
412
0
    hostname_isequal(config->hostname, hostname) &&
413
0
    is_config_in_context(context, config) &&
414
0
    match_netid(config->filter, tags, tag_not_needed))
415
0
  return config;
416
417
  
418
0
  if (!hwaddr)
419
0
    return NULL;
420
421
  /* use match with fewest wildcard octets */
422
0
  for (candidate = NULL, count = 0, config = configs; config; config = config->next)
423
0
    if (is_config_in_context(context, config) &&
424
0
  match_netid(config->filter, tags, tag_not_needed))
425
0
      for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
426
0
  if (conf_addr->wildcard_mask != 0 &&
427
0
      conf_addr->hwaddr_len == hw_len && 
428
0
      (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
429
0
      (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
430
0
    {
431
0
        count = new;
432
0
        candidate = config;
433
0
    }
434
  
435
0
  return candidate;
436
0
}
437
438
/* Find tagged configs first. */
439
struct dhcp_config *find_config(struct dhcp_config *configs,
440
        struct dhcp_context *context,
441
        unsigned char *clid, int clid_len,
442
        unsigned char *hwaddr, int hw_len, 
443
        int hw_type, char *hostname, struct dhcp_netid *tags)
444
0
{
445
0
  struct dhcp_config *ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 0);
446
447
0
  if (!ret)
448
0
    ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 1);
449
450
0
  return ret;
451
0
}
452
453
void dhcp_update_configs(struct dhcp_config *configs)
454
0
{
455
  /* Some people like to keep all static IP addresses in /etc/hosts.
456
     This goes through /etc/hosts and sets static addresses for any DHCP config
457
     records which don't have an address and whose name matches. 
458
     We take care to maintain the invariant that any IP address can appear
459
     in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, 
460
     restore the status-quo ante first. */
461
  
462
0
  struct dhcp_config *config, *conf_tmp;
463
0
  struct crec *crec;
464
0
  int prot = AF_INET;
465
466
0
  for (config = configs; config; config = config->next)
467
0
  {
468
0
    if (config->flags & CONFIG_ADDR_HOSTS)
469
0
      config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
470
0
#ifdef HAVE_DHCP6
471
0
    if (config->flags & CONFIG_ADDR6_HOSTS)
472
0
      config->flags &= ~(CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS);
473
0
#endif
474
0
  }
475
476
0
#ifdef HAVE_DHCP6 
477
0
 again:  
478
0
#endif
479
480
0
  if (daemon->port != 0)
481
0
    for (config = configs; config; config = config->next)
482
0
      {
483
0
  int conflags = CONFIG_ADDR;
484
0
  int cacheflags = F_IPV4;
485
486
0
#ifdef HAVE_DHCP6
487
0
  if (prot == AF_INET6)
488
0
    {
489
0
      conflags = CONFIG_ADDR6;
490
0
      cacheflags = F_IPV6;
491
0
    }
492
0
#endif
493
0
  if (!(config->flags & conflags) &&
494
0
      (config->flags & CONFIG_NAME) && 
495
0
      (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
496
0
      (crec->flags & F_HOSTS))
497
0
    {
498
0
      if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
499
0
        {
500
    /* use primary (first) address */
501
0
    while (crec && !(crec->flags & F_REVERSE))
502
0
      crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
503
0
    if (!crec)
504
0
      continue; /* should be never */
505
0
    inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
506
0
    my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), 
507
0
        config->hostname, daemon->addrbuff);
508
0
        }
509
      
510
0
      if (prot == AF_INET && 
511
0
    (!(conf_tmp = config_find_by_address(configs, crec->addr.addr4)) || conf_tmp == config))
512
0
        {
513
0
    config->addr = crec->addr.addr4;
514
0
    config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
515
0
    continue;
516
0
        }
517
518
0
#ifdef HAVE_DHCP6
519
0
      if (prot == AF_INET6 && 
520
0
    (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config))
521
0
        {
522
    /* host must have exactly one address if comming from /etc/hosts. */
523
0
    if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist))))
524
0
      {
525
0
        config->addr6->next = NULL;
526
0
        config->addr6->flags = 0;
527
0
      }
528
529
0
    if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX)))
530
0
      {
531
0
        memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ);
532
0
        config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
533
0
      }
534
      
535
0
    continue;
536
0
        }
537
0
#endif
538
539
0
      inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
540
0
      my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), 
541
0
          daemon->addrbuff, config->hostname);
542
      
543
      
544
0
    }
545
0
      }
546
547
0
#ifdef HAVE_DHCP6
548
0
  if (prot == AF_INET)
549
0
    {
550
0
      prot = AF_INET6;
551
0
      goto again;
552
0
    }
553
0
#endif
554
555
0
}
556
557
#ifdef HAVE_LINUX_NETWORK 
558
char *whichdevice(void)
559
0
{
560
  /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
561
     to that device. This is for the use case of  (eg) OpenStack, which runs a new
562
     dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE, 
563
     individual processes don't always see the packets they should.
564
     SO_BINDTODEVICE is only available Linux. 
565
566
     Note that if wildcards are used in --interface, or --interface is not used at all,
567
     or a configured interface doesn't yet exist, then more interfaces may arrive later, 
568
     so we can't safely assert there is only one interface and proceed.
569
*/
570
  
571
0
  struct irec *iface, *found;
572
0
  struct iname *if_tmp;
573
  
574
0
  if (!daemon->if_names)
575
0
    return NULL;
576
  
577
0
  for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
578
0
    if (if_tmp->name && (!(if_tmp->flags & INAME_USED) || strchr(if_tmp->name, '*')))
579
0
      return NULL;
580
581
0
  for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
582
0
    if (iface->dhcp4_ok || iface->dhcp6_ok)
583
0
      {
584
0
  if (!found)
585
0
    found = iface;
586
0
  else if (strcmp(found->name, iface->name) != 0) 
587
0
    return NULL; /* more than one. */
588
0
      }
589
590
0
  if (found)
591
0
    {
592
0
      char *ret = safe_malloc(strlen(found->name)+1);
593
0
      strcpy(ret, found->name);
594
0
      return ret;
595
0
    }
596
  
597
0
  return NULL;
598
0
}
599
 
600
static int bindtodevice(char *device, int fd)
601
0
{
602
0
  size_t len = strlen(device)+1;
603
0
  if (len > IFNAMSIZ)
604
0
    len = IFNAMSIZ;
605
  /* only allowed by root. */
606
0
  if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
607
0
      errno != EPERM)
608
0
    return 2;
609
  
610
0
  return 1;
611
0
}
612
613
int bind_dhcp_devices(char *bound_device)
614
0
{
615
0
  int ret = 0;
616
617
0
  if (bound_device)
618
0
    {
619
0
      if (daemon->dhcp)
620
0
  {
621
0
    if (!daemon->relay4)
622
0
      ret |= bindtodevice(bound_device, daemon->dhcpfd);
623
    
624
0
    if (daemon->enable_pxe && daemon->pxefd != -1)
625
0
      ret |= bindtodevice(bound_device, daemon->pxefd);
626
0
  }
627
      
628
0
#if defined(HAVE_DHCP6)
629
0
      if (daemon->doing_dhcp6 && !daemon->relay6)
630
0
  ret |= bindtodevice(bound_device, daemon->dhcp6fd);
631
0
#endif
632
0
    }
633
  
634
0
  return ret;
635
0
}
636
#endif
637
638
static const struct opttab_t {
639
  char *name;
640
  u16 val, size;
641
} opttab[] = {
642
  { "netmask", 1, OT_ADDR_LIST },
643
  { "time-offset", 2, 4 },
644
  { "router", 3, OT_ADDR_LIST  },
645
  { "dns-server", 6, OT_ADDR_LIST },
646
  { "log-server", 7, OT_ADDR_LIST },
647
  { "lpr-server", 9, OT_ADDR_LIST },
648
  { "hostname", 12, OT_INTERNAL | OT_NAME },
649
  { "boot-file-size", 13, 2 | OT_DEC },
650
  { "domain-name", 15, OT_NAME },
651
  { "swap-server", 16, OT_ADDR_LIST },
652
  { "root-path", 17, OT_NAME },
653
  { "extension-path", 18, OT_NAME },
654
  { "ip-forward-enable", 19, 1 },
655
  { "non-local-source-routing", 20, 1 },
656
  { "policy-filter", 21, OT_ADDR_LIST },
657
  { "max-datagram-reassembly", 22, 2 | OT_DEC },
658
  { "default-ttl", 23, 1 | OT_DEC },
659
  { "mtu", 26, 2 | OT_DEC },
660
  { "all-subnets-local", 27, 1 },
661
  { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
662
  { "router-discovery", 31, 1 },
663
  { "router-solicitation", 32, OT_ADDR_LIST },
664
  { "static-route", 33, OT_ADDR_LIST },
665
  { "trailer-encapsulation", 34, 1 },
666
  { "arp-timeout", 35, 4 | OT_DEC },
667
  { "ethernet-encap", 36, 1 },
668
  { "tcp-ttl", 37, 1 },
669
  { "tcp-keepalive", 38, 4 | OT_DEC },
670
  { "nis-domain", 40, OT_NAME },
671
  { "nis-server", 41, OT_ADDR_LIST },
672
  { "ntp-server", 42, OT_ADDR_LIST },
673
  { "vendor-encap", 43, OT_INTERNAL },
674
  { "netbios-ns", 44, OT_ADDR_LIST },
675
  { "netbios-dd", 45, OT_ADDR_LIST },
676
  { "netbios-nodetype", 46, 1 },
677
  { "netbios-scope", 47, 0 },
678
  { "x-windows-fs", 48, OT_ADDR_LIST },
679
  { "x-windows-dm", 49, OT_ADDR_LIST },
680
  { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
681
  { "lease-time", 51, OT_INTERNAL | OT_TIME },
682
  { "option-overload", 52, OT_INTERNAL },
683
  { "message-type", 53, OT_INTERNAL | OT_DEC },
684
  { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
685
  { "parameter-request", 55, OT_INTERNAL },
686
  { "message", 56, OT_INTERNAL },
687
  { "max-message-size", 57, OT_INTERNAL },
688
  { "T1", 58, OT_TIME},
689
  { "T2", 59, OT_TIME},
690
  { "vendor-class", 60, 0 },
691
  { "client-id", 61, OT_INTERNAL },
692
  { "nis+-domain", 64, OT_NAME },
693
  { "nis+-server", 65, OT_ADDR_LIST },
694
  { "tftp-server", 66, OT_NAME },
695
  { "bootfile-name", 67, OT_NAME },
696
  { "mobile-ip-home", 68, OT_ADDR_LIST }, 
697
  { "smtp-server", 69, OT_ADDR_LIST }, 
698
  { "pop3-server", 70, OT_ADDR_LIST }, 
699
  { "nntp-server", 71, OT_ADDR_LIST }, 
700
  { "irc-server", 74, OT_ADDR_LIST }, 
701
  { "user-class", 77, 0 },
702
  { "rapid-commit", 80, 0 },
703
  { "FQDN", 81, OT_INTERNAL },
704
  { "agent-info", 82, OT_INTERNAL },
705
  { "last-transaction", 91, 4 | OT_TIME },
706
  { "associated-ip", 92, OT_ADDR_LIST },
707
  { "client-arch", 93, 2 | OT_DEC },
708
  { "client-interface-id", 94, 0 },
709
  { "client-machine-id", 97, 0 },
710
  { "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
711
  { "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
712
  { "ipv6-only", 108, 4 | OT_DEC },  /* RFC 8925 */ 
713
  { "subnet-select", 118, OT_INTERNAL },
714
  { "domain-search", 119, OT_RFC1035_NAME },
715
  { "sip-server", 120, 0 },
716
  { "classless-static-route", 121, 0 },
717
  { "vendor-id-encap", 125, 0 },
718
  { "tftp-server-address", 150, OT_ADDR_LIST },
719
  { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
720
  { NULL, 0, 0 }
721
};
722
723
#ifdef HAVE_DHCP6
724
static const struct opttab_t opttab6[] = {
725
  { "client-id", 1, OT_INTERNAL },
726
  { "server-id", 2, OT_INTERNAL },
727
  { "ia-na", 3, OT_INTERNAL },
728
  { "ia-ta", 4, OT_INTERNAL },
729
  { "iaaddr", 5, OT_INTERNAL },
730
  { "oro", 6, OT_INTERNAL },
731
  { "preference", 7, OT_INTERNAL | OT_DEC },
732
  { "unicast", 12, OT_INTERNAL },
733
  { "status", 13, OT_INTERNAL },
734
  { "rapid-commit", 14, OT_INTERNAL },
735
  { "user-class", 15, OT_INTERNAL | OT_CSTRING },
736
  { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
737
  { "vendor-opts", 17, OT_INTERNAL },
738
  { "sip-server-domain", 21,  OT_RFC1035_NAME },
739
  { "sip-server", 22, OT_ADDR_LIST },
740
  { "dns-server", 23, OT_ADDR_LIST },
741
  { "domain-search", 24, OT_RFC1035_NAME },
742
  { "nis-server", 27, OT_ADDR_LIST },
743
  { "nis+-server", 28, OT_ADDR_LIST },
744
  { "nis-domain", 29,  OT_RFC1035_NAME },
745
  { "nis+-domain", 30, OT_RFC1035_NAME },
746
  { "sntp-server", 31,  OT_ADDR_LIST },
747
  { "information-refresh-time", 32, OT_TIME },
748
  { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
749
  { "posix-timezone", 41, OT_NAME }, /* RFC 4833, Sec. 3 */
750
  { "tzdb-timezone", 42, OT_NAME }, /* RFC 4833, Sec. 3 */
751
  { "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
752
  { "bootfile-url", 59, OT_NAME },
753
  { "bootfile-param", 60, OT_CSTRING },
754
  { NULL, 0, 0 }
755
};
756
#endif
757
758
759
760
void display_opts(void)
761
0
{
762
0
  int i;
763
  
764
0
  printf(_("Known DHCP options:\n"));
765
  
766
0
  for (i = 0; opttab[i].name; i++)
767
0
    if (!(opttab[i].size & OT_INTERNAL))
768
0
      printf("%3d %s\n", opttab[i].val, opttab[i].name);
769
0
}
770
771
#ifdef HAVE_DHCP6
772
void display_opts6(void)
773
0
{
774
0
  int i;
775
0
  printf(_("Known DHCPv6 options:\n"));
776
  
777
0
  for (i = 0; opttab6[i].name; i++)
778
0
    if (!(opttab6[i].size & OT_INTERNAL))
779
0
      printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
780
0
}
781
#endif
782
783
int lookup_dhcp_opt(int prot, char *name)
784
0
{
785
0
  const struct opttab_t *t;
786
0
  int i;
787
788
0
  (void)prot;
789
790
0
#ifdef HAVE_DHCP6
791
0
  if (prot == AF_INET6)
792
0
    t = opttab6;
793
0
  else
794
0
#endif
795
0
    t = opttab;
796
797
0
  for (i = 0; t[i].name; i++)
798
0
    if (strcasecmp(t[i].name, name) == 0)
799
0
      return t[i].val;
800
  
801
0
  return -1;
802
0
}
803
804
int lookup_dhcp_len(int prot, int val)
805
0
{
806
0
  const struct opttab_t *t;
807
0
  int i;
808
809
0
  (void)prot;
810
811
0
#ifdef HAVE_DHCP6
812
0
  if (prot == AF_INET6)
813
0
    t = opttab6;
814
0
  else
815
0
#endif
816
0
    t = opttab;
817
818
0
  for (i = 0; t[i].name; i++)
819
0
    if (val == t[i].val)
820
0
      return t[i].size & ~OT_DEC;
821
822
0
   return 0;
823
0
}
824
825
char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
826
0
{
827
0
  int o, i, j, nodecode = 0;
828
0
  const struct opttab_t *ot = opttab;
829
830
0
#ifdef HAVE_DHCP6
831
0
  if (prot == AF_INET6)
832
0
    ot = opttab6;
833
0
#endif
834
835
0
  for (o = 0; ot[o].name; o++)
836
0
    if (ot[o].val == opt)
837
0
      {
838
0
  if (buf)
839
0
    {
840
0
      memset(buf, 0, buf_len);
841
      
842
0
      if (ot[o].size & OT_ADDR_LIST) 
843
0
        {
844
0
    union all_addr addr;
845
0
    int addr_len = INADDRSZ;
846
847
0
#ifdef HAVE_DHCP6
848
0
    if (prot == AF_INET6)
849
0
      addr_len = IN6ADDRSZ;
850
0
#endif
851
0
    for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len) 
852
0
      {
853
0
        if (i != 0)
854
0
          strncat(buf, ", ", buf_len - strlen(buf));
855
        /* align */
856
0
        memcpy(&addr, &val[i], addr_len); 
857
0
        inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
858
0
        strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
859
0
      }
860
0
        }
861
0
      else if (ot[o].size & OT_NAME)
862
0
    for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
863
0
      {
864
0
        char c = val[i];
865
0
        if (isprint((unsigned char)c))
866
0
          buf[j++] = c;
867
0
      }
868
0
#ifdef HAVE_DHCP6
869
      /* We don't handle compressed rfc1035 names, so no good in IPv4 land */
870
0
      else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
871
0
        {
872
0
    i = 0, j = 0;
873
0
    while (i < opt_len && val[i] != 0)
874
0
      {
875
0
        int k, l = i + val[i] + 1;
876
0
        for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
877
0
         {
878
0
           char c = val[k];
879
0
           if (isprint((unsigned char)c))
880
0
       buf[j++] = c;
881
0
         }
882
0
        i = l;
883
0
        if (val[i] != 0 && j < buf_len)
884
0
          buf[j++] = '.';
885
0
      }
886
0
        }
887
0
      else if ((ot[o].size & OT_CSTRING))
888
0
        {
889
0
    int k, len;
890
0
    unsigned char *p;
891
892
0
    i = 0, j = 0;
893
0
    while (1)
894
0
      {
895
0
        p = &val[i];
896
0
        GETSHORT(len, p);
897
0
        for (k = 0; k < len && j < buf_len; k++)
898
0
          {
899
0
           char c = *p++;
900
0
           if (isprint((unsigned char)c))
901
0
       buf[j++] = c;
902
0
         }
903
0
        i += len +2;
904
0
        if (i >= opt_len)
905
0
          break;
906
907
0
        if (j < buf_len)
908
0
          buf[j++] = ',';
909
0
      }
910
0
        }       
911
0
#endif
912
0
      else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
913
0
        {
914
0
    unsigned int dec = 0;
915
    
916
0
    for (i = 0; i < opt_len; i++)
917
0
      dec = (dec << 8) | val[i]; 
918
919
0
    if (ot[o].size & OT_TIME)
920
0
      prettyprint_time(buf, dec);
921
0
    else
922
0
      sprintf(buf, "%u", dec);
923
0
        }
924
0
      else
925
0
        nodecode = 1;
926
0
    }
927
0
  break;
928
0
      }
929
930
0
  if (opt_len != 0 && buf && (!ot[o].name || nodecode))
931
0
    {
932
0
      int trunc  = 0;
933
0
      if (opt_len > 14)
934
0
  {
935
0
    trunc = 1;
936
0
    opt_len = 14;
937
0
  }
938
0
      print_mac(buf, val, opt_len);
939
0
      if (trunc)
940
0
  strncat(buf, "...", buf_len - strlen(buf));
941
    
942
943
0
    }
944
945
0
  return ot[o].name ? ot[o].name : "";
946
947
0
}
948
949
void log_context(int family, struct dhcp_context *context)
950
0
{
951
  /* Cannot use dhcp_buff* for RA contexts */
952
953
0
  void *start = &context->start;
954
0
  void *end = &context->end;
955
0
  char *template = "", *p = daemon->namebuff;
956
  
957
0
  *p = 0;
958
    
959
0
#ifdef HAVE_DHCP6
960
0
  if (family == AF_INET6)
961
0
    {
962
0
      struct in6_addr subnet = context->start6;
963
0
      if (!(context->flags & CONTEXT_TEMPLATE))
964
0
  setaddr6part(&subnet, 0);
965
0
      inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN); 
966
0
      start = &context->start6;
967
0
      end = &context->end6;
968
0
    }
969
0
#endif
970
971
0
  if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
972
0
    strcpy(daemon->namebuff, _(", prefix deprecated"));
973
0
  else
974
0
    {
975
0
      p += sprintf(p, _(", lease time "));
976
0
      prettyprint_time(p, context->lease_time);
977
0
      p += strlen(p);
978
0
    } 
979
980
0
#ifdef HAVE_DHCP6
981
0
  if (context->flags & CONTEXT_CONSTRUCTED)
982
0
    {
983
0
      char ifrn_name[IFNAMSIZ];
984
      
985
0
      template = p;
986
0
      p += sprintf(p, ", ");
987
      
988
0
      if (indextoname(daemon->icmp6fd, context->if_index, ifrn_name))
989
0
  sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name);
990
0
    }
991
0
  else if (context->flags & CONTEXT_TEMPLATE && !(context->flags & CONTEXT_RA_STATELESS))
992
0
    {
993
0
      template = p;
994
0
      p += sprintf(p, ", ");
995
      
996
0
      sprintf(p, "template for %s", context->template_interface);  
997
0
    }
998
0
#endif
999
     
1000
0
  if (!(context->flags & CONTEXT_OLD) &&
1001
0
      ((context->flags & CONTEXT_DHCP) || family == AF_INET)) 
1002
0
    {
1003
0
#ifdef HAVE_DHCP6
1004
0
      if (context->flags & CONTEXT_RA_STATELESS)
1005
0
  {
1006
0
    if (context->flags & CONTEXT_TEMPLATE)
1007
0
      strncpy(daemon->dhcp_buff, context->template_interface, DHCP_BUFF_SZ);
1008
0
    else
1009
0
      strcpy(daemon->dhcp_buff, daemon->addrbuff);
1010
0
  }
1011
0
      else 
1012
0
#endif
1013
0
  inet_ntop(family, start, daemon->dhcp_buff, DHCP_BUFF_SZ);
1014
0
      inet_ntop(family, end, daemon->dhcp_buff3, DHCP_BUFF_SZ);
1015
0
      my_syslog(MS_DHCP | LOG_INFO, 
1016
0
    (context->flags & CONTEXT_RA_STATELESS) ? 
1017
0
    _("%s stateless on %s%.0s%.0s%s") :
1018
0
    (context->flags & CONTEXT_STATIC) ? 
1019
0
    _("%s, static leases only on %.0s%s%s%.0s") :
1020
0
    (context->flags & CONTEXT_PROXY) ?
1021
0
    _("%s, proxy on subnet %.0s%s%.0s%.0s") :
1022
0
    _("%s, IP range %s -- %s%s%.0s"),
1023
0
    (family != AF_INET) ? "DHCPv6" : "DHCP",
1024
0
    daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
1025
0
    }
1026
  
1027
0
#ifdef HAVE_DHCP6
1028
0
  if (context->flags & CONTEXT_TEMPLATE)
1029
0
    {
1030
0
      strcpy(daemon->addrbuff, context->template_interface);
1031
0
      template = "";
1032
0
    }
1033
1034
0
  if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
1035
0
    my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
1036
  
1037
0
  if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6)) 
1038
0
    my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
1039
0
#endif
1040
1041
0
}
1042
1043
void log_relay(int family, struct dhcp_relay *relay)
1044
0
{
1045
0
  int broadcast = relay->server.addr4.s_addr == 0;
1046
0
  inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1047
0
  inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
1048
1049
0
  if (family == AF_INET && relay->port != DHCP_SERVER_PORT)
1050
0
    sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port);
1051
1052
0
#ifdef HAVE_DHCP6
1053
0
  struct in6_addr multicast;
1054
1055
0
  inet_pton(AF_INET6, ALL_SERVERS, &multicast);
1056
1057
0
  if (family == AF_INET6)
1058
0
    {
1059
0
      broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast);
1060
0
      if (relay->port != DHCPV6_SERVER_PORT)
1061
0
  sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port);
1062
0
    }
1063
0
#endif
1064
  
1065
  
1066
0
  if (relay->interface)
1067
0
    {
1068
0
      if (broadcast)
1069
0
  my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface);
1070
0
      else if (relay->split_mode)
1071
0
  my_syslog(MS_DHCP | LOG_INFO, _("DHCP split-relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
1072
0
      else
1073
0
  my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
1074
0
    }
1075
0
  else 
1076
0
    my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
1077
0
}
1078
   
1079
#endif