blob: a769fe92eafdf4611f3b02fad796eb35994eeebb [file] [log] [blame]
Simon Kelley2a8710a2020-01-05 16:40:06 +00001/* dnsmasq is Copyright (c) 2000-2020 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{
41 ssize_t sz;
42
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
68 while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR);
69
70 return (msg->msg_flags & MSG_TRUNC) ? -1 : sz;
71}
72
73struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
74{
75 struct tag_if *exprs;
76 struct dhcp_netid_list *list;
77
78 for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
79 if (match_netid(exprs->tag, tags, 1))
80 for (list = exprs->set; list; list = list->next)
81 {
82 list->list->next = tags;
83 tags = list->list;
84 }
85
86 return tags;
87}
88
89
90struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts)
91{
92 struct dhcp_netid *tagif = run_tag_if(tags);
93 struct dhcp_opt *opt;
Simon Kelley96c727f2013-04-02 21:35:41 +010094 struct dhcp_opt *tmp;
Simon Kelley4cb1b322012-02-06 14:30:41 +000095
96 /* flag options which are valid with the current tag set (sans context tags) */
97 for (opt = opts; opt; opt = opt->next)
98 {
99 opt->flags &= ~DHOPT_TAGOK;
100 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
101 match_netid(opt->netid, tagif, 0))
102 opt->flags |= DHOPT_TAGOK;
103 }
104
105 /* now flag options which are valid, including the context tags,
Simon Kelley6caacac2012-02-15 21:58:33 +0000106 otherwise valid options are inhibited if we found a higher priority one above */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000107 if (context_tags)
108 {
109 struct dhcp_netid *last_tag;
110
111 for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
112 last_tag->next = tags;
113 tagif = run_tag_if(context_tags);
114
Simon Kelleya8131112012-03-31 21:35:12 +0100115 /* reset stuff with tag:!<tag> which now matches. */
116 for (opt = opts; opt; opt = opt->next)
117 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
118 (opt->flags & DHOPT_TAGOK) &&
119 !match_netid(opt->netid, tagif, 0))
120 opt->flags &= ~DHOPT_TAGOK;
121
Simon Kelley4cb1b322012-02-06 14:30:41 +0000122 for (opt = opts; opt; opt = opt->next)
123 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
124 match_netid(opt->netid, tagif, 0))
125 {
126 struct dhcp_opt *tmp;
127 for (tmp = opts; tmp; tmp = tmp->next)
128 if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
129 break;
130 if (!tmp)
131 opt->flags |= DHOPT_TAGOK;
132 }
133 }
134
135 /* now flag untagged options which are not overridden by tagged ones */
136 for (opt = opts; opt; opt = opt->next)
137 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
138 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000139 for (tmp = opts; tmp; tmp = tmp->next)
140 if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
141 break;
142 if (!tmp)
143 opt->flags |= DHOPT_TAGOK;
144 else if (!tmp->netid)
145 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
146 }
147
Simon Kelley96c727f2013-04-02 21:35:41 +0100148 /* Finally, eliminate duplicate options later in the chain, and therefore earlier in the config file. */
149 for (opt = opts; opt; opt = opt->next)
150 if (opt->flags & DHOPT_TAGOK)
151 for (tmp = opt->next; tmp; tmp = tmp->next)
152 if (tmp->opt == opt->opt)
153 tmp->flags &= ~DHOPT_TAGOK;
154
Simon Kelley4cb1b322012-02-06 14:30:41 +0000155 return tagif;
156}
157
158/* Is every member of check matched by a member of pool?
159 If tagnotneeded, untagged is OK */
160int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
161{
162 struct dhcp_netid *tmp1;
163
164 if (!check && !tagnotneeded)
165 return 0;
166
167 for (; check; check = check->next)
168 {
169 /* '#' for not is for backwards compat. */
170 if (check->net[0] != '!' && check->net[0] != '#')
171 {
172 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
173 if (strcmp(check->net, tmp1->net) == 0)
174 break;
175 if (!tmp1)
176 return 0;
177 }
178 else
179 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
180 if (strcmp((check->net)+1, tmp1->net) == 0)
181 return 0;
182 }
183 return 1;
184}
185
186/* return domain or NULL if none. */
187char *strip_hostname(char *hostname)
188{
189 char *dot = strchr(hostname, '.');
190
191 if (!dot)
192 return NULL;
193
194 *dot = 0; /* truncate */
195 if (strlen(dot+1) != 0)
196 return dot+1;
197
198 return NULL;
199}
200
201void log_tags(struct dhcp_netid *netid, u32 xid)
202{
203 if (netid && option_bool(OPT_LOG_OPTS))
204 {
205 char *s = daemon->namebuff;
206 for (*s = 0; netid; netid = netid->next)
207 {
208 /* kill dupes. */
209 struct dhcp_netid *n;
210
211 for (n = netid->next; n; n = n->next)
212 if (strcmp(netid->net, n->net) == 0)
213 break;
214
215 if (!n)
216 {
217 strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
218 if (netid->next)
219 strncat (s, ", ", (MAXDNAME-1) - strlen(s));
220 }
221 }
222 my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
223 }
224}
225
Simon Kelley3634c542012-02-08 14:22:37 +0000226int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
227{
228 int i;
229
230 if (o->len > len)
231 return 0;
232
233 if (o->len == 0)
234 return 1;
235
236 if (o->flags & DHOPT_HEX)
237 {
238 if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
239 return 1;
240 }
241 else
242 for (i = 0; i <= (len - o->len); )
243 {
244 if (memcmp(o->val, p + i, o->len) == 0)
245 return 1;
246
247 if (o->flags & DHOPT_STRING)
248 i++;
249 else
250 i += o->len;
251 }
252
253 return 0;
254}
Simon Kelleyceae00d2012-02-09 21:28:14 +0000255
Simon Kelley89500e32013-09-20 16:29:20 +0100256int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
257{
258 struct hwaddr_config *conf_addr;
259
260 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
261 if (conf_addr->wildcard_mask == 0 &&
262 conf_addr->hwaddr_len == len &&
263 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
264 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
265 return 1;
266
267 return 0;
268}
269
270static int is_config_in_context(struct dhcp_context *context, struct dhcp_config *config)
271{
272 if (!context) /* called via find_config() from lease_update_from_configs() */
273 return 1;
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100274
275#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +0000276 if (context->flags & CONTEXT_V6)
277 {
278 struct addrlist *addr_list;
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100279
Simon Kelley137286e2020-02-06 22:09:30 +0000280 if (!(config->flags & CONFIG_ADDR6))
281 return 1;
282
283 for (; context; context = context->current)
284 for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
285 {
286 if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
287 return 1;
288
289 if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
290 return 1;
291 }
292 }
293 else
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100294#endif
Simon Kelley137286e2020-02-06 22:09:30 +0000295 {
296 if (!(config->flags & CONFIG_ADDR))
Simon Kelley89500e32013-09-20 16:29:20 +0100297 return 1;
Simon Kelley137286e2020-02-06 22:09:30 +0000298
299 for (; context; context = context->current)
300 if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
301 return 1;
302 }
Simon Kelley89500e32013-09-20 16:29:20 +0100303
Simon Kelley89500e32013-09-20 16:29:20 +0100304 return 0;
305}
306
Simon Kelley52ec7832020-02-07 21:05:54 +0000307static struct dhcp_config *find_config_match(struct dhcp_config *configs,
308 struct dhcp_context *context,
309 unsigned char *clid, int clid_len,
310 unsigned char *hwaddr, int hw_len,
311 int hw_type, char *hostname,
312 struct dhcp_netid *tags, int tag_not_needed)
Simon Kelley89500e32013-09-20 16:29:20 +0100313{
314 int count, new;
315 struct dhcp_config *config, *candidate;
316 struct hwaddr_config *conf_addr;
317
318 if (clid)
319 for (config = configs; config; config = config->next)
320 if (config->flags & CONFIG_CLID)
321 {
322 if (config->clid_len == clid_len &&
323 memcmp(config->clid, clid, clid_len) == 0 &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000324 is_config_in_context(context, config) &&
325 match_netid(config->filter, tags, tag_not_needed))
326
Simon Kelley89500e32013-09-20 16:29:20 +0100327 return config;
328
329 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
René van Dorst53c4c5c2013-10-18 13:53:05 +0100330 cope with that here. This is IPv4 only. context==NULL implies IPv4,
331 see lease_update_from_configs() */
332 if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1 &&
Simon Kelley89500e32013-09-20 16:29:20 +0100333 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000334 is_config_in_context(context, config) &&
335 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100336 return config;
337 }
338
339
340 if (hwaddr)
341 for (config = configs; config; config = config->next)
342 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000343 is_config_in_context(context, config) &&
344 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100345 return config;
346
347 if (hostname && context)
348 for (config = configs; config; config = config->next)
349 if ((config->flags & CONFIG_NAME) &&
350 hostname_isequal(config->hostname, hostname) &&
Simon Kelley52ec7832020-02-07 21:05:54 +0000351 is_config_in_context(context, config) &&
352 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100353 return config;
354
355
356 if (!hwaddr)
357 return NULL;
358
359 /* use match with fewest wildcard octets */
360 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
Simon Kelley52ec7832020-02-07 21:05:54 +0000361 if (is_config_in_context(context, config) &&
362 match_netid(config->filter, tags, tag_not_needed))
Simon Kelley89500e32013-09-20 16:29:20 +0100363 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
364 if (conf_addr->wildcard_mask != 0 &&
365 conf_addr->hwaddr_len == hw_len &&
366 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
367 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
368 {
369 count = new;
370 candidate = config;
371 }
372
373 return candidate;
374}
375
Simon Kelley52ec7832020-02-07 21:05:54 +0000376/* Find tagged configs first. */
377struct dhcp_config *find_config(struct dhcp_config *configs,
378 struct dhcp_context *context,
379 unsigned char *clid, int clid_len,
380 unsigned char *hwaddr, int hw_len,
381 int hw_type, char *hostname, struct dhcp_netid *tags)
382{
383 struct dhcp_config *ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 0);
384
385 if (!ret)
386 ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 1);
387
388 return ret;
389}
390
Simon Kelleyceae00d2012-02-09 21:28:14 +0000391void dhcp_update_configs(struct dhcp_config *configs)
392{
393 /* Some people like to keep all static IP addresses in /etc/hosts.
394 This goes through /etc/hosts and sets static addresses for any DHCP config
395 records which don't have an address and whose name matches.
396 We take care to maintain the invariant that any IP address can appear
397 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
398 restore the status-quo ante first. */
399
Simon Kelley35239a32012-09-24 15:09:33 +0100400 struct dhcp_config *config, *conf_tmp;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000401 struct crec *crec;
402 int prot = AF_INET;
403
404 for (config = configs; config; config = config->next)
Steven Silotid2d49902019-01-17 22:52:13 +0000405 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000406 if (config->flags & CONFIG_ADDR_HOSTS)
Steven Siloti18eac672019-01-13 22:56:36 +0000407 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
408#ifdef HAVE_DHCP6
409 if (config->flags & CONFIG_ADDR6_HOSTS)
410 config->flags &= ~(CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS);
411#endif
Steven Silotid2d49902019-01-17 22:52:13 +0000412 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000413
414#ifdef HAVE_DHCP6
415 again:
416#endif
417
418 if (daemon->port != 0)
419 for (config = configs; config; config = config->next)
420 {
421 int conflags = CONFIG_ADDR;
422 int cacheflags = F_IPV4;
423
424#ifdef HAVE_DHCP6
425 if (prot == AF_INET6)
426 {
427 conflags = CONFIG_ADDR6;
428 cacheflags = F_IPV6;
429 }
430#endif
431 if (!(config->flags & conflags) &&
432 (config->flags & CONFIG_NAME) &&
433 (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
434 (crec->flags & F_HOSTS))
435 {
436 if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
437 {
438 /* use primary (first) address */
Simon Kelley00238fb2013-12-18 13:24:12 +0000439 while (crec && !(crec->flags & F_REVERSE))
440 crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
441 if (!crec)
442 continue; /* should be never */
Simon Kelleycc921df2019-01-02 22:48:59 +0000443 inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley00238fb2013-12-18 13:24:12 +0000444 my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
445 config->hostname, daemon->addrbuff);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000446 }
447
Simon Kelley35239a32012-09-24 15:09:33 +0100448 if (prot == AF_INET &&
Simon Kelleycc921df2019-01-02 22:48:59 +0000449 (!(conf_tmp = config_find_by_address(configs, crec->addr.addr4)) || conf_tmp == config))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000450 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000451 config->addr = crec->addr.addr4;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000452 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
453 continue;
454 }
455
456#ifdef HAVE_DHCP6
Simon Kelley35239a32012-09-24 15:09:33 +0100457 if (prot == AF_INET6 &&
Simon Kelley79aba0f2020-02-03 23:58:45 +0000458 (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000459 {
Simon Kelley137286e2020-02-06 22:09:30 +0000460 /* host must have exactly one address if comming from /etc/hosts. */
461 if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist))))
462 {
463 config->addr6->next = NULL;
464 config->addr6->flags = 0;
465 }
466
467 if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX)))
468 {
469 memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ);
470 config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
471 }
472
Simon Kelleyceae00d2012-02-09 21:28:14 +0000473 continue;
474 }
475#endif
476
Simon Kelleycc921df2019-01-02 22:48:59 +0000477 inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000478 my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
479 daemon->addrbuff, config->hostname);
480
481
482 }
483 }
484
485#ifdef HAVE_DHCP6
486 if (prot == AF_INET)
487 {
488 prot = AF_INET6;
489 goto again;
490 }
491#endif
492
493}
Simon Kelley4cb1b322012-02-06 14:30:41 +0000494
Simon Kelley9380ba72012-04-16 14:41:56 +0100495#ifdef HAVE_LINUX_NETWORK
Simon Kelley3b3f4412013-10-11 16:33:28 +0100496char *whichdevice(void)
Simon Kelley9380ba72012-04-16 14:41:56 +0100497{
498 /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
499 to that device. This is for the use case of (eg) OpenStack, which runs a new
500 dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE,
501 individual processes don't always see the packets they should.
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100502 SO_BINDTODEVICE is only available Linux.
503
Simon Kelley8584c502013-10-10 21:15:23 +0100504 Note that if wildcards are used in --interface, or --interface is not used at all,
505 or a configured interface doesn't yet exist, then more interfaces may arrive later,
506 so we can't safely assert there is only one interface and proceed.
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100507*/
Simon Kelley9380ba72012-04-16 14:41:56 +0100508
509 struct irec *iface, *found;
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100510 struct iname *if_tmp;
Simon Kelley3b3f4412013-10-11 16:33:28 +0100511
Simon Kelley8584c502013-10-10 21:15:23 +0100512 if (!daemon->if_names)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100513 return NULL;
514
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100515 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
516 if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*')))
Simon Kelley3b3f4412013-10-11 16:33:28 +0100517 return NULL;
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100518
Simon Kelley9380ba72012-04-16 14:41:56 +0100519 for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
520 if (iface->dhcp_ok)
521 {
522 if (!found)
523 found = iface;
524 else if (strcmp(found->name, iface->name) != 0)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100525 return NULL; /* more than one. */
Simon Kelley9380ba72012-04-16 14:41:56 +0100526 }
Simon Kelley3b3f4412013-10-11 16:33:28 +0100527
Simon Kelley9380ba72012-04-16 14:41:56 +0100528 if (found)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100529 return found->name;
530
531 return NULL;
532}
533
534void bindtodevice(char *device, int fd)
535{
Petr Menšík47b45b22018-08-15 18:17:00 +0200536 size_t len = strlen(device)+1;
537 if (len > IFNAMSIZ)
538 len = IFNAMSIZ;
Simon Kelley3b3f4412013-10-11 16:33:28 +0100539 /* only allowed by root. */
Petr Menšík47b45b22018-08-15 18:17:00 +0200540 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
Simon Kelley3b3f4412013-10-11 16:33:28 +0100541 errno != EPERM)
542 die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley9380ba72012-04-16 14:41:56 +0100543}
544#endif
Simon Kelley40ef23b2012-03-13 21:59:28 +0000545
546static const struct opttab_t {
547 char *name;
548 u16 val, size;
549} opttab[] = {
550 { "netmask", 1, OT_ADDR_LIST },
551 { "time-offset", 2, 4 },
552 { "router", 3, OT_ADDR_LIST },
553 { "dns-server", 6, OT_ADDR_LIST },
554 { "log-server", 7, OT_ADDR_LIST },
555 { "lpr-server", 9, OT_ADDR_LIST },
556 { "hostname", 12, OT_INTERNAL | OT_NAME },
557 { "boot-file-size", 13, 2 | OT_DEC },
558 { "domain-name", 15, OT_NAME },
559 { "swap-server", 16, OT_ADDR_LIST },
560 { "root-path", 17, OT_NAME },
561 { "extension-path", 18, OT_NAME },
562 { "ip-forward-enable", 19, 1 },
563 { "non-local-source-routing", 20, 1 },
564 { "policy-filter", 21, OT_ADDR_LIST },
565 { "max-datagram-reassembly", 22, 2 | OT_DEC },
566 { "default-ttl", 23, 1 | OT_DEC },
567 { "mtu", 26, 2 | OT_DEC },
568 { "all-subnets-local", 27, 1 },
569 { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
570 { "router-discovery", 31, 1 },
571 { "router-solicitation", 32, OT_ADDR_LIST },
572 { "static-route", 33, OT_ADDR_LIST },
573 { "trailer-encapsulation", 34, 1 },
574 { "arp-timeout", 35, 4 | OT_DEC },
575 { "ethernet-encap", 36, 1 },
576 { "tcp-ttl", 37, 1 },
577 { "tcp-keepalive", 38, 4 | OT_DEC },
578 { "nis-domain", 40, OT_NAME },
579 { "nis-server", 41, OT_ADDR_LIST },
580 { "ntp-server", 42, OT_ADDR_LIST },
581 { "vendor-encap", 43, OT_INTERNAL },
582 { "netbios-ns", 44, OT_ADDR_LIST },
583 { "netbios-dd", 45, OT_ADDR_LIST },
584 { "netbios-nodetype", 46, 1 },
585 { "netbios-scope", 47, 0 },
586 { "x-windows-fs", 48, OT_ADDR_LIST },
587 { "x-windows-dm", 49, OT_ADDR_LIST },
588 { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100589 { "lease-time", 51, OT_INTERNAL | OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000590 { "option-overload", 52, OT_INTERNAL },
591 { "message-type", 53, OT_INTERNAL | OT_DEC },
592 { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
593 { "parameter-request", 55, OT_INTERNAL },
594 { "message", 56, OT_INTERNAL },
595 { "max-message-size", 57, OT_INTERNAL },
Simon Kelleyca85a282015-05-13 22:33:04 +0100596 { "T1", 58, OT_TIME},
597 { "T2", 59, OT_TIME},
Simon Kelley40ef23b2012-03-13 21:59:28 +0000598 { "vendor-class", 60, 0 },
599 { "client-id", 61, OT_INTERNAL },
600 { "nis+-domain", 64, OT_NAME },
601 { "nis+-server", 65, OT_ADDR_LIST },
602 { "tftp-server", 66, OT_NAME },
603 { "bootfile-name", 67, OT_NAME },
604 { "mobile-ip-home", 68, OT_ADDR_LIST },
605 { "smtp-server", 69, OT_ADDR_LIST },
606 { "pop3-server", 70, OT_ADDR_LIST },
607 { "nntp-server", 71, OT_ADDR_LIST },
608 { "irc-server", 74, OT_ADDR_LIST },
609 { "user-class", 77, 0 },
Simon Kelley734d5312018-03-23 23:09:53 +0000610 { "rapid-commit", 80, 0 },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000611 { "FQDN", 81, OT_INTERNAL },
612 { "agent-id", 82, OT_INTERNAL },
613 { "client-arch", 93, 2 | OT_DEC },
614 { "client-interface-id", 94, 0 },
615 { "client-machine-id", 97, 0 },
616 { "subnet-select", 118, OT_INTERNAL },
617 { "domain-search", 119, OT_RFC1035_NAME },
618 { "sip-server", 120, 0 },
619 { "classless-static-route", 121, 0 },
620 { "vendor-id-encap", 125, 0 },
Ville Skyttä0c211c42019-12-05 17:11:09 +0000621 { "tftp-server-address", 150, OT_ADDR_LIST },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000622 { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
623 { NULL, 0, 0 }
624};
625
626#ifdef HAVE_DHCP6
627static const struct opttab_t opttab6[] = {
628 { "client-id", 1, OT_INTERNAL },
629 { "server-id", 2, OT_INTERNAL },
630 { "ia-na", 3, OT_INTERNAL },
631 { "ia-ta", 4, OT_INTERNAL },
632 { "iaaddr", 5, OT_INTERNAL },
633 { "oro", 6, OT_INTERNAL },
634 { "preference", 7, OT_INTERNAL | OT_DEC },
635 { "unicast", 12, OT_INTERNAL },
636 { "status", 13, OT_INTERNAL },
637 { "rapid-commit", 14, OT_INTERNAL },
638 { "user-class", 15, OT_INTERNAL | OT_CSTRING },
639 { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
640 { "vendor-opts", 17, OT_INTERNAL },
641 { "sip-server-domain", 21, OT_RFC1035_NAME },
642 { "sip-server", 22, OT_ADDR_LIST },
643 { "dns-server", 23, OT_ADDR_LIST },
644 { "domain-search", 24, OT_RFC1035_NAME },
645 { "nis-server", 27, OT_ADDR_LIST },
646 { "nis+-server", 28, OT_ADDR_LIST },
647 { "nis-domain", 29, OT_RFC1035_NAME },
648 { "nis+-domain", 30, OT_RFC1035_NAME },
649 { "sntp-server", 31, OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100650 { "information-refresh-time", 32, OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000651 { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
Simon Kelley102208d2015-09-10 21:50:00 +0100652 { "ntp-server", 56, 0 },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000653 { "bootfile-url", 59, OT_NAME },
654 { "bootfile-param", 60, OT_CSTRING },
655 { NULL, 0, 0 }
656};
657#endif
658
659
660
661void display_opts(void)
662{
663 int i;
664
665 printf(_("Known DHCP options:\n"));
666
667 for (i = 0; opttab[i].name; i++)
668 if (!(opttab[i].size & OT_INTERNAL))
669 printf("%3d %s\n", opttab[i].val, opttab[i].name);
670}
671
672#ifdef HAVE_DHCP6
673void display_opts6(void)
674{
675 int i;
676 printf(_("Known DHCPv6 options:\n"));
677
678 for (i = 0; opttab6[i].name; i++)
679 if (!(opttab6[i].size & OT_INTERNAL))
680 printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
681}
682#endif
683
Simon Kelleybd08ae62013-04-19 10:22:06 +0100684int lookup_dhcp_opt(int prot, char *name)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000685{
686 const struct opttab_t *t;
687 int i;
688
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100689 (void)prot;
690
Simon Kelley40ef23b2012-03-13 21:59:28 +0000691#ifdef HAVE_DHCP6
692 if (prot == AF_INET6)
693 t = opttab6;
694 else
695#endif
696 t = opttab;
697
698 for (i = 0; t[i].name; i++)
Simon Kelleyc7961072013-02-28 15:17:58 +0000699 if (strcasecmp(t[i].name, name) == 0)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000700 return t[i].val;
701
Simon Kelleybd08ae62013-04-19 10:22:06 +0100702 return -1;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000703}
704
Simon Kelleybd08ae62013-04-19 10:22:06 +0100705int lookup_dhcp_len(int prot, int val)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000706{
707 const struct opttab_t *t;
708 int i;
709
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100710 (void)prot;
711
Simon Kelley40ef23b2012-03-13 21:59:28 +0000712#ifdef HAVE_DHCP6
713 if (prot == AF_INET6)
714 t = opttab6;
715 else
716#endif
717 t = opttab;
718
719 for (i = 0; t[i].name; i++)
720 if (val == t[i].val)
Simon Kelleyc7961072013-02-28 15:17:58 +0000721 return t[i].size & ~OT_DEC;
722
723 return 0;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000724}
725
726char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
727{
728 int o, i, j, nodecode = 0;
729 const struct opttab_t *ot = opttab;
730
731#ifdef HAVE_DHCP6
732 if (prot == AF_INET6)
733 ot = opttab6;
734#endif
735
736 for (o = 0; ot[o].name; o++)
737 if (ot[o].val == opt)
738 {
739 if (buf)
740 {
741 memset(buf, 0, buf_len);
742
743 if (ot[o].size & OT_ADDR_LIST)
744 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000745 union all_addr addr;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000746 int addr_len = INADDRSZ;
747
748#ifdef HAVE_DHCP6
749 if (prot == AF_INET6)
750 addr_len = IN6ADDRSZ;
751#endif
752 for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
753 {
754 if (i != 0)
755 strncat(buf, ", ", buf_len - strlen(buf));
756 /* align */
757 memcpy(&addr, &val[i], addr_len);
758 inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
759 strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
760 }
761 }
762 else if (ot[o].size & OT_NAME)
763 for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
764 {
765 char c = val[i];
766 if (isprint((int)c))
767 buf[j++] = c;
768 }
769#ifdef HAVE_DHCP6
770 /* We don't handle compressed rfc1035 names, so no good in IPv4 land */
771 else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
772 {
773 i = 0, j = 0;
774 while (i < opt_len && val[i] != 0)
775 {
776 int k, l = i + val[i] + 1;
777 for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
778 {
779 char c = val[k];
780 if (isprint((int)c))
781 buf[j++] = c;
782 }
783 i = l;
784 if (val[i] != 0 && j < buf_len)
785 buf[j++] = '.';
786 }
787 }
788 else if ((ot[o].size & OT_CSTRING))
789 {
790 int k, len;
791 unsigned char *p;
792
793 i = 0, j = 0;
794 while (1)
795 {
796 p = &val[i];
797 GETSHORT(len, p);
798 for (k = 0; k < len && j < buf_len; k++)
799 {
800 char c = *p++;
801 if (isprint((int)c))
802 buf[j++] = c;
803 }
804 i += len +2;
805 if (i >= opt_len)
806 break;
807
808 if (j < buf_len)
809 buf[j++] = ',';
810 }
811 }
812#endif
Simon Kelley23245c02012-07-18 16:21:11 +0100813 else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000814 {
815 unsigned int dec = 0;
816
817 for (i = 0; i < opt_len; i++)
818 dec = (dec << 8) | val[i];
819
Simon Kelley23245c02012-07-18 16:21:11 +0100820 if (ot[o].size & OT_TIME)
821 prettyprint_time(buf, dec);
822 else
823 sprintf(buf, "%u", dec);
Simon Kelley40ef23b2012-03-13 21:59:28 +0000824 }
825 else
826 nodecode = 1;
827 }
828 break;
829 }
830
831 if (opt_len != 0 && buf && (!ot[o].name || nodecode))
832 {
833 int trunc = 0;
834 if (opt_len > 14)
835 {
836 trunc = 1;
837 opt_len = 14;
838 }
839 print_mac(buf, val, opt_len);
840 if (trunc)
841 strncat(buf, "...", buf_len - strlen(buf));
842
843
844 }
845
846 return ot[o].name ? ot[o].name : "";
847
848}
849
Simon Kelley1f776932012-12-16 19:46:08 +0000850void log_context(int family, struct dhcp_context *context)
851{
852 /* Cannot use dhcp_buff* for RA contexts */
853
854 void *start = &context->start;
855 void *end = &context->end;
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000856 char *template = "", *p = daemon->namebuff;
Simon Kelleyc1be9172012-12-17 22:37:30 +0000857
858 *p = 0;
859
Simon Kelley1f776932012-12-16 19:46:08 +0000860#ifdef HAVE_DHCP6
861 if (family == AF_INET6)
862 {
863 struct in6_addr subnet = context->start6;
864 if (!(context->flags & CONTEXT_TEMPLATE))
865 setaddr6part(&subnet, 0);
866 inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN);
867 start = &context->start6;
868 end = &context->end6;
869 }
870#endif
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000871
872 if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
873 strcpy(daemon->namebuff, _(", prefix deprecated"));
874 else
Simon Kelley1f776932012-12-16 19:46:08 +0000875 {
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000876 p += sprintf(p, _(", lease time "));
877 prettyprint_time(p, context->lease_time);
878 p += strlen(p);
879 }
880
Simon Kelleyc1be9172012-12-17 22:37:30 +0000881#ifdef HAVE_DHCP6
882 if (context->flags & CONTEXT_CONSTRUCTED)
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000883 {
884 char ifrn_name[IFNAMSIZ];
885
886 template = p;
887 p += sprintf(p, ", ");
888
Simon Kelleya8105592013-09-25 15:36:00 +0100889 if (indextoname(daemon->icmp6fd, context->if_index, ifrn_name))
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100890 sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name);
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000891 }
Simon Kelley903650a2013-10-03 11:43:09 +0100892 else if (context->flags & CONTEXT_TEMPLATE && !(context->flags & CONTEXT_RA_STATELESS))
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000893 {
894 template = p;
895 p += sprintf(p, ", ");
Simon Kelley903650a2013-10-03 11:43:09 +0100896
Simon Kelley49333cb2013-03-15 20:30:51 +0000897 sprintf(p, "template for %s", context->template_interface);
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000898 }
Simon Kelleyc1be9172012-12-17 22:37:30 +0000899#endif
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000900
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100901 if (!(context->flags & CONTEXT_OLD) &&
902 ((context->flags & CONTEXT_DHCP) || family == AF_INET))
Simon Kelley1f776932012-12-16 19:46:08 +0000903 {
Simon Kelley903650a2013-10-03 11:43:09 +0100904#ifdef HAVE_DHCP6
905 if (context->flags & CONTEXT_RA_STATELESS)
906 {
907 if (context->flags & CONTEXT_TEMPLATE)
Simon Kelleybf4e62c2016-07-22 21:37:59 +0100908 strncpy(daemon->dhcp_buff, context->template_interface, DHCP_BUFF_SZ);
Simon Kelley903650a2013-10-03 11:43:09 +0100909 else
910 strcpy(daemon->dhcp_buff, daemon->addrbuff);
911 }
912 else
913#endif
Simon Kelleybf4e62c2016-07-22 21:37:59 +0100914 inet_ntop(family, start, daemon->dhcp_buff, DHCP_BUFF_SZ);
915 inet_ntop(family, end, daemon->dhcp_buff3, DHCP_BUFF_SZ);
Simon Kelley1f776932012-12-16 19:46:08 +0000916 my_syslog(MS_DHCP | LOG_INFO,
Simon Kelley903650a2013-10-03 11:43:09 +0100917 (context->flags & CONTEXT_RA_STATELESS) ?
918 _("%s stateless on %s%.0s%.0s%s") :
919 (context->flags & CONTEXT_STATIC) ?
920 _("%s, static leases only on %.0s%s%s%.0s") :
921 (context->flags & CONTEXT_PROXY) ?
922 _("%s, proxy on subnet %.0s%s%.0s%.0s") :
923 _("%s, IP range %s -- %s%s%.0s"),
924 (family != AF_INET) ? "DHCPv6" : "DHCP",
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000925 daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
Simon Kelley1f776932012-12-16 19:46:08 +0000926 }
927
Simon Kelleyc1be9172012-12-17 22:37:30 +0000928#ifdef HAVE_DHCP6
Simon Kelleye4e9b342013-10-02 11:00:45 +0100929 if (context->flags & CONTEXT_TEMPLATE)
930 {
931 strcpy(daemon->addrbuff, context->template_interface);
932 template = "";
933 }
Simon Kelley903650a2013-10-03 11:43:09 +0100934
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100935 if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000936 my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100937
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000938 if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6))
939 my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
Simon Kelleyc1be9172012-12-17 22:37:30 +0000940#endif
941
Simon Kelley1f776932012-12-16 19:46:08 +0000942}
Simon Kelley1f776932012-12-16 19:46:08 +0000943
Simon Kelleyff7eea22013-09-04 18:01:38 +0100944void log_relay(int family, struct dhcp_relay *relay)
945{
946 inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
947 inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
948
949 if (relay->interface)
950 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
951 else
952 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
953}
954
Simon Kelley4cb1b322012-02-06 14:30:41 +0000955#endif