blob: 36bc38aee4174d37542e1496c71193f5599e93ac [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
Simon Kelley4cb1b322012-02-06 14:30:41 +00002
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
21void dhcp_common_init(void)
22{
Simon Kelleybf4e62c2016-07-22 21:37:59 +010023 /* These each hold a DHCP option max size 255
24 and get a terminating zero added */
25 daemon->dhcp_buff = safe_malloc(DHCP_BUFF_SZ);
26 daemon->dhcp_buff2 = safe_malloc(DHCP_BUFF_SZ);
27 daemon->dhcp_buff3 = safe_malloc(DHCP_BUFF_SZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +000028
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 expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet));
33#ifdef HAVE_DHCP6
34 if (daemon->dhcp6)
35 expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
36#endif
37}
38
39ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
40{
Simon Kelley4890bcd2020-04-28 14:02:53 +010041 ssize_t sz, new_sz;
Simon Kelley4cb1b322012-02-06 14:30:41 +000042
43 while (1)
44 {
45 msg->msg_flags = 0;
46 while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
47
48 if (sz == -1)
49 return -1;
50
51 if (!(msg->msg_flags & MSG_TRUNC))
52 break;
53
54 /* Very new Linux kernels return the actual size needed,
55 older ones always return truncated size */
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000056 if ((size_t)sz == msg->msg_iov->iov_len)
Simon Kelley4cb1b322012-02-06 14:30:41 +000057 {
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000058 if (!expand_buf(msg->msg_iov, sz + 100))
Simon Kelley4cb1b322012-02-06 14:30:41 +000059 return -1;
60 }
61 else
62 {
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000063 expand_buf(msg->msg_iov, sz);
Simon Kelley4cb1b322012-02-06 14:30:41 +000064 break;
65 }
66 }
67
Simon Kelley4890bcd2020-04-28 14:02:53 +010068 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. */
Simon Kelley4cb1b322012-02-06 14:30:41 +000075
Simon Kelley4890bcd2020-04-28 14:02:53 +010076 if (new_sz == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
77 new_sz = sz;
78
79 return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
Simon Kelley4cb1b322012-02-06 14:30:41 +000080}
81
82struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
83{
84 struct tag_if *exprs;
85 struct dhcp_netid_list *list;
86
87 for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
88 if (match_netid(exprs->tag, tags, 1))
89 for (list = exprs->set; list; list = list->next)
90 {
91 list->list->next = tags;
92 tags = list->list;
93 }
94
95 return tags;
96}
97
98
99struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts)
100{
101 struct dhcp_netid *tagif = run_tag_if(tags);
102 struct dhcp_opt *opt;
Simon Kelley96c727f2013-04-02 21:35:41 +0100103 struct dhcp_opt *tmp;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000104
105 /* flag options which are valid with the current tag set (sans context tags) */
106 for (opt = opts; opt; opt = opt->next)
107 {
108 opt->flags &= ~DHOPT_TAGOK;
109 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
110 match_netid(opt->netid, tagif, 0))
111 opt->flags |= DHOPT_TAGOK;
112 }
113
114 /* now flag options which are valid, including the context tags,
Simon Kelley6caacac2012-02-15 21:58:33 +0000115 otherwise valid options are inhibited if we found a higher priority one above */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000116 if (context_tags)
117 {
118 struct dhcp_netid *last_tag;
119
120 for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
121 last_tag->next = tags;
122 tagif = run_tag_if(context_tags);
123
Simon Kelleya8131112012-03-31 21:35:12 +0100124 /* reset stuff with tag:!<tag> which now matches. */
125 for (opt = opts; opt; opt = opt->next)
126 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
127 (opt->flags & DHOPT_TAGOK) &&
128 !match_netid(opt->netid, tagif, 0))
129 opt->flags &= ~DHOPT_TAGOK;
130
Simon Kelley4cb1b322012-02-06 14:30:41 +0000131 for (opt = opts; opt; opt = opt->next)
132 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
133 match_netid(opt->netid, tagif, 0))
134 {
135 struct dhcp_opt *tmp;
136 for (tmp = opts; tmp; tmp = tmp->next)
137 if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
138 break;
139 if (!tmp)
140 opt->flags |= DHOPT_TAGOK;
141 }
142 }
143
144 /* now flag untagged options which are not overridden by tagged ones */
145 for (opt = opts; opt; opt = opt->next)
146 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
147 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000148 for (tmp = opts; tmp; tmp = tmp->next)
149 if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
150 break;
151 if (!tmp)
152 opt->flags |= DHOPT_TAGOK;
153 else if (!tmp->netid)
154 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
155 }
156
Simon Kelley96c727f2013-04-02 21:35:41 +0100157 /* Finally, eliminate duplicate options later in the chain, and therefore earlier in the config file. */
158 for (opt = opts; opt; opt = opt->next)
159 if (opt->flags & DHOPT_TAGOK)
160 for (tmp = opt->next; tmp; tmp = tmp->next)
161 if (tmp->opt == opt->opt)
162 tmp->flags &= ~DHOPT_TAGOK;
163
Simon Kelley4cb1b322012-02-06 14:30:41 +0000164 return tagif;
165}
166
167/* Is every member of check matched by a member of pool?
168 If tagnotneeded, untagged is OK */
169int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
170{
171 struct dhcp_netid *tmp1;
172
173 if (!check && !tagnotneeded)
174 return 0;
175
176 for (; check; check = check->next)
177 {
178 /* '#' for not is for backwards compat. */
179 if (check->net[0] != '!' && check->net[0] != '#')
180 {
181 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
182 if (strcmp(check->net, tmp1->net) == 0)
183 break;
184 if (!tmp1)
185 return 0;
186 }
187 else
188 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
189 if (strcmp((check->net)+1, tmp1->net) == 0)
190 return 0;
191 }
192 return 1;
193}
194
195/* return domain or NULL if none. */
196char *strip_hostname(char *hostname)
197{
198 char *dot = strchr(hostname, '.');
199
200 if (!dot)
201 return NULL;
202
203 *dot = 0; /* truncate */
204 if (strlen(dot+1) != 0)
205 return dot+1;
206
207 return NULL;
208}
209
210void log_tags(struct dhcp_netid *netid, u32 xid)
211{
212 if (netid && option_bool(OPT_LOG_OPTS))
213 {
214 char *s = daemon->namebuff;
215 for (*s = 0; netid; netid = netid->next)
216 {
217 /* kill dupes. */
218 struct dhcp_netid *n;
219
220 for (n = netid->next; n; n = n->next)
221 if (strcmp(netid->net, n->net) == 0)
222 break;
223
224 if (!n)
225 {
226 strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
227 if (netid->next)
228 strncat (s, ", ", (MAXDNAME-1) - strlen(s));
229 }
230 }
231 my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
232 }
233}
234
Simon Kelley3634c542012-02-08 14:22:37 +0000235int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
236{
237 int i;
238
239 if (o->len > len)
240 return 0;
241
242 if (o->len == 0)
243 return 1;
244
245 if (o->flags & DHOPT_HEX)
246 {
247 if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
248 return 1;
249 }
250 else
251 for (i = 0; i <= (len - o->len); )
252 {
253 if (memcmp(o->val, p + i, o->len) == 0)
254 return 1;
255
256 if (o->flags & DHOPT_STRING)
257 i++;
258 else
259 i += o->len;
260 }
261
262 return 0;
263}
Simon Kelleyceae00d2012-02-09 21:28:14 +0000264
Simon Kelley89500e32013-09-20 16:29:20 +0100265int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
266{
267 struct hwaddr_config *conf_addr;
268
269 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
270 if (conf_addr->wildcard_mask == 0 &&
271 conf_addr->hwaddr_len == len &&
272 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
273 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
274 return 1;
275
276 return 0;
277}
278
279static int is_config_in_context(struct dhcp_context *context, struct dhcp_config *config)
280{
281 if (!context) /* called via find_config() from lease_update_from_configs() */
282 return 1;
Simon Kelleye7c0d7b2021-02-28 17:56:54 +0000283
284 if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
285 return 1;
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100286
287#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +0000288 if (context->flags & CONTEXT_V6)
289 {
290 struct addrlist *addr_list;
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100291
Simon Kelleye7c0d7b2021-02-28 17:56:54 +0000292 if (config->flags & CONFIG_ADDR6)
293 for (; context; context = context->current)
294 for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
295 {
296 if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
297 return 1;
298
299 if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
300 return 1;
301 }
Simon Kelley137286e2020-02-06 22:09:30 +0000302 }
303 else
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100304#endif
Simon Kelley137286e2020-02-06 22:09:30 +0000305 {
Simon Kelley137286e2020-02-06 22:09:30 +0000306 for (; context; context = context->current)
307 if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
308 return 1;
309 }
Simon Kelley89500e32013-09-20 16:29:20 +0100310
Simon Kelley89500e32013-09-20 16:29:20 +0100311 return 0;
312}
313
Simon Kelley52ec7832020-02-07 21:05:54 +0000314static struct dhcp_config *find_config_match(struct dhcp_config *configs,
315 struct dhcp_context *context,
316 unsigned char *clid, int clid_len,
317 unsigned char *hwaddr, int hw_len,
318 int hw_type, char *hostname,
319 struct dhcp_netid *tags, int tag_not_needed)
Simon Kelley89500e32013-09-20 16:29:20 +0100320{
321 int count, new;
322 struct dhcp_config *config, *candidate;
323 struct hwaddr_config *conf_addr;
324
325 if (clid)
326 for (config = configs; config; config = config->next)
327 if (config->flags & CONFIG_CLID)
328 {
329 if (config->clid_len == clid_len &&
330 memcmp(config->clid, clid, clid_len) == 0 &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000331 is_config_in_context(context, config) &&
332 match_netid(config->filter, tags, tag_not_needed))
333
Simon Kelley89500e32013-09-20 16:29:20 +0100334 return config;
335
336 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
René van Dorst53c4c5c2013-10-18 13:53:05 +0100337 cope with that here. This is IPv4 only. context==NULL implies IPv4,
338 see lease_update_from_configs() */
339 if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1 &&
Simon Kelley89500e32013-09-20 16:29:20 +0100340 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000341 is_config_in_context(context, config) &&
342 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100343 return config;
344 }
345
346
347 if (hwaddr)
348 for (config = configs; config; config = config->next)
349 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000350 is_config_in_context(context, config) &&
351 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100352 return config;
353
354 if (hostname && context)
355 for (config = configs; config; config = config->next)
356 if ((config->flags & CONFIG_NAME) &&
357 hostname_isequal(config->hostname, hostname) &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000358 is_config_in_context(context, config) &&
359 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100360 return config;
361
362
363 if (!hwaddr)
364 return NULL;
365
366 /* use match with fewest wildcard octets */
367 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
Simon Kelley52ec7832020-02-07 21:05:54 +0000368 if (is_config_in_context(context, config) &&
369 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100370 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
371 if (conf_addr->wildcard_mask != 0 &&
372 conf_addr->hwaddr_len == hw_len &&
373 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
374 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
375 {
376 count = new;
377 candidate = config;
378 }
379
380 return candidate;
381}
382
Simon Kelley52ec7832020-02-07 21:05:54 +0000383/* Find tagged configs first. */
384struct dhcp_config *find_config(struct dhcp_config *configs,
385 struct dhcp_context *context,
386 unsigned char *clid, int clid_len,
387 unsigned char *hwaddr, int hw_len,
388 int hw_type, char *hostname, struct dhcp_netid *tags)
389{
390 struct dhcp_config *ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 0);
391
392 if (!ret)
393 ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 1);
394
395 return ret;
396}
397
Simon Kelleyceae00d2012-02-09 21:28:14 +0000398void dhcp_update_configs(struct dhcp_config *configs)
399{
400 /* Some people like to keep all static IP addresses in /etc/hosts.
401 This goes through /etc/hosts and sets static addresses for any DHCP config
402 records which don't have an address and whose name matches.
403 We take care to maintain the invariant that any IP address can appear
404 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
405 restore the status-quo ante first. */
406
Simon Kelley35239a32012-09-24 15:09:33 +0100407 struct dhcp_config *config, *conf_tmp;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000408 struct crec *crec;
409 int prot = AF_INET;
410
411 for (config = configs; config; config = config->next)
Steven Silotid2d49902019-01-17 22:52:13 +0000412 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000413 if (config->flags & CONFIG_ADDR_HOSTS)
Steven Siloti18eac672019-01-13 22:56:36 +0000414 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
415#ifdef HAVE_DHCP6
416 if (config->flags & CONFIG_ADDR6_HOSTS)
417 config->flags &= ~(CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS);
418#endif
Steven Silotid2d49902019-01-17 22:52:13 +0000419 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000420
421#ifdef HAVE_DHCP6
422 again:
423#endif
424
425 if (daemon->port != 0)
426 for (config = configs; config; config = config->next)
427 {
428 int conflags = CONFIG_ADDR;
429 int cacheflags = F_IPV4;
430
431#ifdef HAVE_DHCP6
432 if (prot == AF_INET6)
433 {
434 conflags = CONFIG_ADDR6;
435 cacheflags = F_IPV6;
436 }
437#endif
438 if (!(config->flags & conflags) &&
439 (config->flags & CONFIG_NAME) &&
440 (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
441 (crec->flags & F_HOSTS))
442 {
443 if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
444 {
445 /* use primary (first) address */
Simon Kelley00238fb2013-12-18 13:24:12 +0000446 while (crec && !(crec->flags & F_REVERSE))
447 crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
448 if (!crec)
449 continue; /* should be never */
Simon Kelleycc921df2019-01-02 22:48:59 +0000450 inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley00238fb2013-12-18 13:24:12 +0000451 my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
452 config->hostname, daemon->addrbuff);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000453 }
454
Simon Kelley35239a32012-09-24 15:09:33 +0100455 if (prot == AF_INET &&
Simon Kelleycc921df2019-01-02 22:48:59 +0000456 (!(conf_tmp = config_find_by_address(configs, crec->addr.addr4)) || conf_tmp == config))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000457 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000458 config->addr = crec->addr.addr4;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000459 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
460 continue;
461 }
462
463#ifdef HAVE_DHCP6
Simon Kelley35239a32012-09-24 15:09:33 +0100464 if (prot == AF_INET6 &&
Simon Kelley79aba0f2020-02-03 23:58:45 +0000465 (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000466 {
Simon Kelley137286e2020-02-06 22:09:30 +0000467 /* host must have exactly one address if comming from /etc/hosts. */
468 if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist))))
469 {
470 config->addr6->next = NULL;
471 config->addr6->flags = 0;
472 }
473
474 if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX)))
475 {
476 memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ);
477 config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
478 }
479
Simon Kelleyceae00d2012-02-09 21:28:14 +0000480 continue;
481 }
482#endif
483
Simon Kelleycc921df2019-01-02 22:48:59 +0000484 inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000485 my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
486 daemon->addrbuff, config->hostname);
487
488
489 }
490 }
491
492#ifdef HAVE_DHCP6
493 if (prot == AF_INET)
494 {
495 prot = AF_INET6;
496 goto again;
497 }
498#endif
499
500}
Simon Kelley4cb1b322012-02-06 14:30:41 +0000501
Simon Kelley9380ba72012-04-16 14:41:56 +0100502#ifdef HAVE_LINUX_NETWORK
Simon Kelley3b3f4412013-10-11 16:33:28 +0100503char *whichdevice(void)
Simon Kelley9380ba72012-04-16 14:41:56 +0100504{
505 /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
506 to that device. This is for the use case of (eg) OpenStack, which runs a new
507 dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE,
508 individual processes don't always see the packets they should.
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100509 SO_BINDTODEVICE is only available Linux.
510
Simon Kelley8584c502013-10-10 21:15:23 +0100511 Note that if wildcards are used in --interface, or --interface is not used at all,
512 or a configured interface doesn't yet exist, then more interfaces may arrive later,
513 so we can't safely assert there is only one interface and proceed.
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100514*/
Simon Kelley9380ba72012-04-16 14:41:56 +0100515
516 struct irec *iface, *found;
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100517 struct iname *if_tmp;
Simon Kelley3b3f4412013-10-11 16:33:28 +0100518
Simon Kelley8584c502013-10-10 21:15:23 +0100519 if (!daemon->if_names)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100520 return NULL;
521
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100522 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
523 if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*')))
Simon Kelley3b3f4412013-10-11 16:33:28 +0100524 return NULL;
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100525
Simon Kelley9380ba72012-04-16 14:41:56 +0100526 for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
527 if (iface->dhcp_ok)
528 {
529 if (!found)
530 found = iface;
531 else if (strcmp(found->name, iface->name) != 0)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100532 return NULL; /* more than one. */
Simon Kelley9380ba72012-04-16 14:41:56 +0100533 }
Simon Kelley3b3f4412013-10-11 16:33:28 +0100534
Simon Kelley9380ba72012-04-16 14:41:56 +0100535 if (found)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100536 return found->name;
537
538 return NULL;
539}
540
541void bindtodevice(char *device, int fd)
542{
Petr Menšík47b45b22018-08-15 18:17:00 +0200543 size_t len = strlen(device)+1;
544 if (len > IFNAMSIZ)
545 len = IFNAMSIZ;
Simon Kelley3b3f4412013-10-11 16:33:28 +0100546 /* only allowed by root. */
Petr Menšík47b45b22018-08-15 18:17:00 +0200547 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
Simon Kelley3b3f4412013-10-11 16:33:28 +0100548 errno != EPERM)
549 die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley9380ba72012-04-16 14:41:56 +0100550}
551#endif
Simon Kelley40ef23b2012-03-13 21:59:28 +0000552
553static const struct opttab_t {
554 char *name;
555 u16 val, size;
556} opttab[] = {
557 { "netmask", 1, OT_ADDR_LIST },
558 { "time-offset", 2, 4 },
559 { "router", 3, OT_ADDR_LIST },
560 { "dns-server", 6, OT_ADDR_LIST },
561 { "log-server", 7, OT_ADDR_LIST },
562 { "lpr-server", 9, OT_ADDR_LIST },
563 { "hostname", 12, OT_INTERNAL | OT_NAME },
564 { "boot-file-size", 13, 2 | OT_DEC },
565 { "domain-name", 15, OT_NAME },
566 { "swap-server", 16, OT_ADDR_LIST },
567 { "root-path", 17, OT_NAME },
568 { "extension-path", 18, OT_NAME },
569 { "ip-forward-enable", 19, 1 },
570 { "non-local-source-routing", 20, 1 },
571 { "policy-filter", 21, OT_ADDR_LIST },
572 { "max-datagram-reassembly", 22, 2 | OT_DEC },
573 { "default-ttl", 23, 1 | OT_DEC },
574 { "mtu", 26, 2 | OT_DEC },
575 { "all-subnets-local", 27, 1 },
576 { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
577 { "router-discovery", 31, 1 },
578 { "router-solicitation", 32, OT_ADDR_LIST },
579 { "static-route", 33, OT_ADDR_LIST },
580 { "trailer-encapsulation", 34, 1 },
581 { "arp-timeout", 35, 4 | OT_DEC },
582 { "ethernet-encap", 36, 1 },
583 { "tcp-ttl", 37, 1 },
584 { "tcp-keepalive", 38, 4 | OT_DEC },
585 { "nis-domain", 40, OT_NAME },
586 { "nis-server", 41, OT_ADDR_LIST },
587 { "ntp-server", 42, OT_ADDR_LIST },
588 { "vendor-encap", 43, OT_INTERNAL },
589 { "netbios-ns", 44, OT_ADDR_LIST },
590 { "netbios-dd", 45, OT_ADDR_LIST },
591 { "netbios-nodetype", 46, 1 },
592 { "netbios-scope", 47, 0 },
593 { "x-windows-fs", 48, OT_ADDR_LIST },
594 { "x-windows-dm", 49, OT_ADDR_LIST },
595 { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100596 { "lease-time", 51, OT_INTERNAL | OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000597 { "option-overload", 52, OT_INTERNAL },
598 { "message-type", 53, OT_INTERNAL | OT_DEC },
599 { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
600 { "parameter-request", 55, OT_INTERNAL },
601 { "message", 56, OT_INTERNAL },
602 { "max-message-size", 57, OT_INTERNAL },
Simon Kelleyca85a282015-05-13 22:33:04 +0100603 { "T1", 58, OT_TIME},
604 { "T2", 59, OT_TIME},
Simon Kelley40ef23b2012-03-13 21:59:28 +0000605 { "vendor-class", 60, 0 },
606 { "client-id", 61, OT_INTERNAL },
607 { "nis+-domain", 64, OT_NAME },
608 { "nis+-server", 65, OT_ADDR_LIST },
609 { "tftp-server", 66, OT_NAME },
610 { "bootfile-name", 67, OT_NAME },
611 { "mobile-ip-home", 68, OT_ADDR_LIST },
612 { "smtp-server", 69, OT_ADDR_LIST },
613 { "pop3-server", 70, OT_ADDR_LIST },
614 { "nntp-server", 71, OT_ADDR_LIST },
615 { "irc-server", 74, OT_ADDR_LIST },
616 { "user-class", 77, 0 },
Simon Kelley734d5312018-03-23 23:09:53 +0000617 { "rapid-commit", 80, 0 },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000618 { "FQDN", 81, OT_INTERNAL },
619 { "agent-id", 82, OT_INTERNAL },
620 { "client-arch", 93, 2 | OT_DEC },
621 { "client-interface-id", 94, 0 },
622 { "client-machine-id", 97, 0 },
623 { "subnet-select", 118, OT_INTERNAL },
624 { "domain-search", 119, OT_RFC1035_NAME },
625 { "sip-server", 120, 0 },
626 { "classless-static-route", 121, 0 },
627 { "vendor-id-encap", 125, 0 },
Ville Skyttä0c211c42019-12-05 17:11:09 +0000628 { "tftp-server-address", 150, OT_ADDR_LIST },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000629 { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
630 { NULL, 0, 0 }
631};
632
633#ifdef HAVE_DHCP6
634static const struct opttab_t opttab6[] = {
635 { "client-id", 1, OT_INTERNAL },
636 { "server-id", 2, OT_INTERNAL },
637 { "ia-na", 3, OT_INTERNAL },
638 { "ia-ta", 4, OT_INTERNAL },
639 { "iaaddr", 5, OT_INTERNAL },
640 { "oro", 6, OT_INTERNAL },
641 { "preference", 7, OT_INTERNAL | OT_DEC },
642 { "unicast", 12, OT_INTERNAL },
643 { "status", 13, OT_INTERNAL },
644 { "rapid-commit", 14, OT_INTERNAL },
645 { "user-class", 15, OT_INTERNAL | OT_CSTRING },
646 { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
647 { "vendor-opts", 17, OT_INTERNAL },
648 { "sip-server-domain", 21, OT_RFC1035_NAME },
649 { "sip-server", 22, OT_ADDR_LIST },
650 { "dns-server", 23, OT_ADDR_LIST },
651 { "domain-search", 24, OT_RFC1035_NAME },
652 { "nis-server", 27, OT_ADDR_LIST },
653 { "nis+-server", 28, OT_ADDR_LIST },
654 { "nis-domain", 29, OT_RFC1035_NAME },
655 { "nis+-domain", 30, OT_RFC1035_NAME },
656 { "sntp-server", 31, OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100657 { "information-refresh-time", 32, OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000658 { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
Vladislav Grishenkodded78b2020-03-08 15:34:34 +0000659 { "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000660 { "bootfile-url", 59, OT_NAME },
661 { "bootfile-param", 60, OT_CSTRING },
662 { NULL, 0, 0 }
663};
664#endif
665
666
667
668void display_opts(void)
669{
670 int i;
671
672 printf(_("Known DHCP options:\n"));
673
674 for (i = 0; opttab[i].name; i++)
675 if (!(opttab[i].size & OT_INTERNAL))
676 printf("%3d %s\n", opttab[i].val, opttab[i].name);
677}
678
679#ifdef HAVE_DHCP6
680void display_opts6(void)
681{
682 int i;
683 printf(_("Known DHCPv6 options:\n"));
684
685 for (i = 0; opttab6[i].name; i++)
686 if (!(opttab6[i].size & OT_INTERNAL))
687 printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
688}
689#endif
690
Simon Kelleybd08ae62013-04-19 10:22:06 +0100691int lookup_dhcp_opt(int prot, char *name)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000692{
693 const struct opttab_t *t;
694 int i;
695
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100696 (void)prot;
697
Simon Kelley40ef23b2012-03-13 21:59:28 +0000698#ifdef HAVE_DHCP6
699 if (prot == AF_INET6)
700 t = opttab6;
701 else
702#endif
703 t = opttab;
704
705 for (i = 0; t[i].name; i++)
Simon Kelleyc7961072013-02-28 15:17:58 +0000706 if (strcasecmp(t[i].name, name) == 0)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000707 return t[i].val;
708
Simon Kelleybd08ae62013-04-19 10:22:06 +0100709 return -1;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000710}
711
Simon Kelleybd08ae62013-04-19 10:22:06 +0100712int lookup_dhcp_len(int prot, int val)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000713{
714 const struct opttab_t *t;
715 int i;
716
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100717 (void)prot;
718
Simon Kelley40ef23b2012-03-13 21:59:28 +0000719#ifdef HAVE_DHCP6
720 if (prot == AF_INET6)
721 t = opttab6;
722 else
723#endif
724 t = opttab;
725
726 for (i = 0; t[i].name; i++)
727 if (val == t[i].val)
Simon Kelleyc7961072013-02-28 15:17:58 +0000728 return t[i].size & ~OT_DEC;
729
730 return 0;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000731}
732
733char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
734{
735 int o, i, j, nodecode = 0;
736 const struct opttab_t *ot = opttab;
737
738#ifdef HAVE_DHCP6
739 if (prot == AF_INET6)
740 ot = opttab6;
741#endif
742
743 for (o = 0; ot[o].name; o++)
744 if (ot[o].val == opt)
745 {
746 if (buf)
747 {
748 memset(buf, 0, buf_len);
749
750 if (ot[o].size & OT_ADDR_LIST)
751 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000752 union all_addr addr;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000753 int addr_len = INADDRSZ;
754
755#ifdef HAVE_DHCP6
756 if (prot == AF_INET6)
757 addr_len = IN6ADDRSZ;
758#endif
759 for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
760 {
761 if (i != 0)
762 strncat(buf, ", ", buf_len - strlen(buf));
763 /* align */
764 memcpy(&addr, &val[i], addr_len);
765 inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
766 strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
767 }
768 }
769 else if (ot[o].size & OT_NAME)
770 for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
771 {
772 char c = val[i];
773 if (isprint((int)c))
774 buf[j++] = c;
775 }
776#ifdef HAVE_DHCP6
777 /* We don't handle compressed rfc1035 names, so no good in IPv4 land */
778 else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
779 {
780 i = 0, j = 0;
781 while (i < opt_len && val[i] != 0)
782 {
783 int k, l = i + val[i] + 1;
784 for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
785 {
786 char c = val[k];
787 if (isprint((int)c))
788 buf[j++] = c;
789 }
790 i = l;
791 if (val[i] != 0 && j < buf_len)
792 buf[j++] = '.';
793 }
794 }
795 else if ((ot[o].size & OT_CSTRING))
796 {
797 int k, len;
798 unsigned char *p;
799
800 i = 0, j = 0;
801 while (1)
802 {
803 p = &val[i];
804 GETSHORT(len, p);
805 for (k = 0; k < len && j < buf_len; k++)
806 {
807 char c = *p++;
808 if (isprint((int)c))
809 buf[j++] = c;
810 }
811 i += len +2;
812 if (i >= opt_len)
813 break;
814
815 if (j < buf_len)
816 buf[j++] = ',';
817 }
818 }
819#endif
Simon Kelley23245c02012-07-18 16:21:11 +0100820 else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000821 {
822 unsigned int dec = 0;
823
824 for (i = 0; i < opt_len; i++)
825 dec = (dec << 8) | val[i];
826
Simon Kelley23245c02012-07-18 16:21:11 +0100827 if (ot[o].size & OT_TIME)
828 prettyprint_time(buf, dec);
829 else
830 sprintf(buf, "%u", dec);
Simon Kelley40ef23b2012-03-13 21:59:28 +0000831 }
832 else
833 nodecode = 1;
834 }
835 break;
836 }
837
838 if (opt_len != 0 && buf && (!ot[o].name || nodecode))
839 {
840 int trunc = 0;
841 if (opt_len > 14)
842 {
843 trunc = 1;
844 opt_len = 14;
845 }
846 print_mac(buf, val, opt_len);
847 if (trunc)
848 strncat(buf, "...", buf_len - strlen(buf));
849
850
851 }
852
853 return ot[o].name ? ot[o].name : "";
854
855}
856
Simon Kelley1f776932012-12-16 19:46:08 +0000857void log_context(int family, struct dhcp_context *context)
858{
859 /* Cannot use dhcp_buff* for RA contexts */
860
861 void *start = &context->start;
862 void *end = &context->end;
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000863 char *template = "", *p = daemon->namebuff;
Simon Kelleyc1be9172012-12-17 22:37:30 +0000864
865 *p = 0;
866
Simon Kelley1f776932012-12-16 19:46:08 +0000867#ifdef HAVE_DHCP6
868 if (family == AF_INET6)
869 {
870 struct in6_addr subnet = context->start6;
871 if (!(context->flags & CONTEXT_TEMPLATE))
872 setaddr6part(&subnet, 0);
873 inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN);
874 start = &context->start6;
875 end = &context->end6;
876 }
877#endif
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000878
879 if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
880 strcpy(daemon->namebuff, _(", prefix deprecated"));
881 else
Simon Kelley1f776932012-12-16 19:46:08 +0000882 {
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000883 p += sprintf(p, _(", lease time "));
884 prettyprint_time(p, context->lease_time);
885 p += strlen(p);
886 }
887
Simon Kelleyc1be9172012-12-17 22:37:30 +0000888#ifdef HAVE_DHCP6
889 if (context->flags & CONTEXT_CONSTRUCTED)
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000890 {
891 char ifrn_name[IFNAMSIZ];
892
893 template = p;
894 p += sprintf(p, ", ");
895
Simon Kelleya8105592013-09-25 15:36:00 +0100896 if (indextoname(daemon->icmp6fd, context->if_index, ifrn_name))
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100897 sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name);
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000898 }
Simon Kelley903650a2013-10-03 11:43:09 +0100899 else if (context->flags & CONTEXT_TEMPLATE && !(context->flags & CONTEXT_RA_STATELESS))
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000900 {
901 template = p;
902 p += sprintf(p, ", ");
Simon Kelley903650a2013-10-03 11:43:09 +0100903
Simon Kelley49333cb2013-03-15 20:30:51 +0000904 sprintf(p, "template for %s", context->template_interface);
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000905 }
Simon Kelleyc1be9172012-12-17 22:37:30 +0000906#endif
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000907
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100908 if (!(context->flags & CONTEXT_OLD) &&
909 ((context->flags & CONTEXT_DHCP) || family == AF_INET))
Simon Kelley1f776932012-12-16 19:46:08 +0000910 {
Simon Kelley903650a2013-10-03 11:43:09 +0100911#ifdef HAVE_DHCP6
912 if (context->flags & CONTEXT_RA_STATELESS)
913 {
914 if (context->flags & CONTEXT_TEMPLATE)
Simon Kelleybf4e62c2016-07-22 21:37:59 +0100915 strncpy(daemon->dhcp_buff, context->template_interface, DHCP_BUFF_SZ);
Simon Kelley903650a2013-10-03 11:43:09 +0100916 else
917 strcpy(daemon->dhcp_buff, daemon->addrbuff);
918 }
919 else
920#endif
Simon Kelleybf4e62c2016-07-22 21:37:59 +0100921 inet_ntop(family, start, daemon->dhcp_buff, DHCP_BUFF_SZ);
922 inet_ntop(family, end, daemon->dhcp_buff3, DHCP_BUFF_SZ);
Simon Kelley1f776932012-12-16 19:46:08 +0000923 my_syslog(MS_DHCP | LOG_INFO,
Simon Kelley903650a2013-10-03 11:43:09 +0100924 (context->flags & CONTEXT_RA_STATELESS) ?
925 _("%s stateless on %s%.0s%.0s%s") :
926 (context->flags & CONTEXT_STATIC) ?
927 _("%s, static leases only on %.0s%s%s%.0s") :
928 (context->flags & CONTEXT_PROXY) ?
929 _("%s, proxy on subnet %.0s%s%.0s%.0s") :
930 _("%s, IP range %s -- %s%s%.0s"),
931 (family != AF_INET) ? "DHCPv6" : "DHCP",
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000932 daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
Simon Kelley1f776932012-12-16 19:46:08 +0000933 }
934
Simon Kelleyc1be9172012-12-17 22:37:30 +0000935#ifdef HAVE_DHCP6
Simon Kelleye4e9b342013-10-02 11:00:45 +0100936 if (context->flags & CONTEXT_TEMPLATE)
937 {
938 strcpy(daemon->addrbuff, context->template_interface);
939 template = "";
940 }
Simon Kelley903650a2013-10-03 11:43:09 +0100941
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100942 if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000943 my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100944
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000945 if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6))
946 my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
Simon Kelleyc1be9172012-12-17 22:37:30 +0000947#endif
948
Simon Kelley1f776932012-12-16 19:46:08 +0000949}
Simon Kelley1f776932012-12-16 19:46:08 +0000950
Simon Kelleyff7eea22013-09-04 18:01:38 +0100951void log_relay(int family, struct dhcp_relay *relay)
952{
953 inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
954 inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
955
956 if (relay->interface)
957 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
958 else
959 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
960}
961
Simon Kelley4cb1b322012-02-06 14:30:41 +0000962#endif