blob: a03edc97b6b8dd69ad32176bffce7f46aa998857 [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;
274
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100275 if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
276 return 1;
277
278#ifdef HAVE_DHCP6
279 if ((context->flags & CONTEXT_V6) && (config->flags & CONFIG_WILDCARD))
280 return 1;
281#endif
282
283 for (; context; context = context->current)
284#ifdef HAVE_DHCP6
285 if (context->flags & CONTEXT_V6)
286 {
287 if ((config->flags & CONFIG_ADDR6) && is_same_net6(&config->addr6, &context->start6, context->prefix))
288 return 1;
289 }
290 else
291#endif
292 if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
Simon Kelley89500e32013-09-20 16:29:20 +0100293 return 1;
294
Simon Kelley89500e32013-09-20 16:29:20 +0100295 return 0;
296}
297
298struct dhcp_config *find_config(struct dhcp_config *configs,
299 struct dhcp_context *context,
300 unsigned char *clid, int clid_len,
301 unsigned char *hwaddr, int hw_len,
302 int hw_type, char *hostname)
303{
304 int count, new;
305 struct dhcp_config *config, *candidate;
306 struct hwaddr_config *conf_addr;
307
308 if (clid)
309 for (config = configs; config; config = config->next)
310 if (config->flags & CONFIG_CLID)
311 {
312 if (config->clid_len == clid_len &&
313 memcmp(config->clid, clid, clid_len) == 0 &&
314 is_config_in_context(context, config))
315 return config;
316
317 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
René van Dorst53c4c5c2013-10-18 13:53:05 +0100318 cope with that here. This is IPv4 only. context==NULL implies IPv4,
319 see lease_update_from_configs() */
320 if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1 &&
Simon Kelley89500e32013-09-20 16:29:20 +0100321 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
322 is_config_in_context(context, config))
323 return config;
324 }
325
326
327 if (hwaddr)
328 for (config = configs; config; config = config->next)
329 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
330 is_config_in_context(context, config))
331 return config;
332
333 if (hostname && context)
334 for (config = configs; config; config = config->next)
335 if ((config->flags & CONFIG_NAME) &&
336 hostname_isequal(config->hostname, hostname) &&
337 is_config_in_context(context, config))
338 return config;
339
340
341 if (!hwaddr)
342 return NULL;
343
344 /* use match with fewest wildcard octets */
345 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
346 if (is_config_in_context(context, config))
347 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
348 if (conf_addr->wildcard_mask != 0 &&
349 conf_addr->hwaddr_len == hw_len &&
350 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
351 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
352 {
353 count = new;
354 candidate = config;
355 }
356
357 return candidate;
358}
359
Simon Kelleyceae00d2012-02-09 21:28:14 +0000360void dhcp_update_configs(struct dhcp_config *configs)
361{
362 /* Some people like to keep all static IP addresses in /etc/hosts.
363 This goes through /etc/hosts and sets static addresses for any DHCP config
364 records which don't have an address and whose name matches.
365 We take care to maintain the invariant that any IP address can appear
366 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
367 restore the status-quo ante first. */
368
Simon Kelley35239a32012-09-24 15:09:33 +0100369 struct dhcp_config *config, *conf_tmp;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000370 struct crec *crec;
371 int prot = AF_INET;
372
373 for (config = configs; config; config = config->next)
Steven Silotid2d49902019-01-17 22:52:13 +0000374 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000375 if (config->flags & CONFIG_ADDR_HOSTS)
Steven Siloti18eac672019-01-13 22:56:36 +0000376 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
377#ifdef HAVE_DHCP6
378 if (config->flags & CONFIG_ADDR6_HOSTS)
379 config->flags &= ~(CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS);
380#endif
Steven Silotid2d49902019-01-17 22:52:13 +0000381 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000382
383#ifdef HAVE_DHCP6
384 again:
385#endif
386
387 if (daemon->port != 0)
388 for (config = configs; config; config = config->next)
389 {
390 int conflags = CONFIG_ADDR;
391 int cacheflags = F_IPV4;
392
393#ifdef HAVE_DHCP6
394 if (prot == AF_INET6)
395 {
396 conflags = CONFIG_ADDR6;
397 cacheflags = F_IPV6;
398 }
399#endif
400 if (!(config->flags & conflags) &&
401 (config->flags & CONFIG_NAME) &&
402 (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
403 (crec->flags & F_HOSTS))
404 {
405 if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
406 {
407 /* use primary (first) address */
Simon Kelley00238fb2013-12-18 13:24:12 +0000408 while (crec && !(crec->flags & F_REVERSE))
409 crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
410 if (!crec)
411 continue; /* should be never */
Simon Kelleycc921df2019-01-02 22:48:59 +0000412 inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley00238fb2013-12-18 13:24:12 +0000413 my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
414 config->hostname, daemon->addrbuff);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000415 }
416
Simon Kelley35239a32012-09-24 15:09:33 +0100417 if (prot == AF_INET &&
Simon Kelleycc921df2019-01-02 22:48:59 +0000418 (!(conf_tmp = config_find_by_address(configs, crec->addr.addr4)) || conf_tmp == config))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000419 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000420 config->addr = crec->addr.addr4;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000421 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
422 continue;
423 }
424
425#ifdef HAVE_DHCP6
Simon Kelley35239a32012-09-24 15:09:33 +0100426 if (prot == AF_INET6 &&
Simon Kelley79aba0f2020-02-03 23:58:45 +0000427 (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000428 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000429 memcpy(&config->addr6, &crec->addr.addr6, IN6ADDRSZ);
Steven Siloti18eac672019-01-13 22:56:36 +0000430 config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
Simon Kelley79aba0f2020-02-03 23:58:45 +0000431 config->flags &= ~CONFIG_PREFIX;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000432 continue;
433 }
434#endif
435
Simon Kelleycc921df2019-01-02 22:48:59 +0000436 inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000437 my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
438 daemon->addrbuff, config->hostname);
439
440
441 }
442 }
443
444#ifdef HAVE_DHCP6
445 if (prot == AF_INET)
446 {
447 prot = AF_INET6;
448 goto again;
449 }
450#endif
451
452}
Simon Kelley4cb1b322012-02-06 14:30:41 +0000453
Simon Kelley9380ba72012-04-16 14:41:56 +0100454#ifdef HAVE_LINUX_NETWORK
Simon Kelley3b3f4412013-10-11 16:33:28 +0100455char *whichdevice(void)
Simon Kelley9380ba72012-04-16 14:41:56 +0100456{
457 /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
458 to that device. This is for the use case of (eg) OpenStack, which runs a new
459 dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE,
460 individual processes don't always see the packets they should.
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100461 SO_BINDTODEVICE is only available Linux.
462
Simon Kelley8584c502013-10-10 21:15:23 +0100463 Note that if wildcards are used in --interface, or --interface is not used at all,
464 or a configured interface doesn't yet exist, then more interfaces may arrive later,
465 so we can't safely assert there is only one interface and proceed.
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100466*/
Simon Kelley9380ba72012-04-16 14:41:56 +0100467
468 struct irec *iface, *found;
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100469 struct iname *if_tmp;
Simon Kelley3b3f4412013-10-11 16:33:28 +0100470
Simon Kelley8584c502013-10-10 21:15:23 +0100471 if (!daemon->if_names)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100472 return NULL;
473
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100474 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
475 if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*')))
Simon Kelley3b3f4412013-10-11 16:33:28 +0100476 return NULL;
Simon Kelleye2ba0df2013-05-31 17:04:25 +0100477
Simon Kelley9380ba72012-04-16 14:41:56 +0100478 for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
479 if (iface->dhcp_ok)
480 {
481 if (!found)
482 found = iface;
483 else if (strcmp(found->name, iface->name) != 0)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100484 return NULL; /* more than one. */
Simon Kelley9380ba72012-04-16 14:41:56 +0100485 }
Simon Kelley3b3f4412013-10-11 16:33:28 +0100486
Simon Kelley9380ba72012-04-16 14:41:56 +0100487 if (found)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100488 return found->name;
489
490 return NULL;
491}
492
493void bindtodevice(char *device, int fd)
494{
Petr Menšík47b45b22018-08-15 18:17:00 +0200495 size_t len = strlen(device)+1;
496 if (len > IFNAMSIZ)
497 len = IFNAMSIZ;
Simon Kelley3b3f4412013-10-11 16:33:28 +0100498 /* only allowed by root. */
Petr Menšík47b45b22018-08-15 18:17:00 +0200499 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
Simon Kelley3b3f4412013-10-11 16:33:28 +0100500 errno != EPERM)
501 die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley9380ba72012-04-16 14:41:56 +0100502}
503#endif
Simon Kelley40ef23b2012-03-13 21:59:28 +0000504
505static const struct opttab_t {
506 char *name;
507 u16 val, size;
508} opttab[] = {
509 { "netmask", 1, OT_ADDR_LIST },
510 { "time-offset", 2, 4 },
511 { "router", 3, OT_ADDR_LIST },
512 { "dns-server", 6, OT_ADDR_LIST },
513 { "log-server", 7, OT_ADDR_LIST },
514 { "lpr-server", 9, OT_ADDR_LIST },
515 { "hostname", 12, OT_INTERNAL | OT_NAME },
516 { "boot-file-size", 13, 2 | OT_DEC },
517 { "domain-name", 15, OT_NAME },
518 { "swap-server", 16, OT_ADDR_LIST },
519 { "root-path", 17, OT_NAME },
520 { "extension-path", 18, OT_NAME },
521 { "ip-forward-enable", 19, 1 },
522 { "non-local-source-routing", 20, 1 },
523 { "policy-filter", 21, OT_ADDR_LIST },
524 { "max-datagram-reassembly", 22, 2 | OT_DEC },
525 { "default-ttl", 23, 1 | OT_DEC },
526 { "mtu", 26, 2 | OT_DEC },
527 { "all-subnets-local", 27, 1 },
528 { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
529 { "router-discovery", 31, 1 },
530 { "router-solicitation", 32, OT_ADDR_LIST },
531 { "static-route", 33, OT_ADDR_LIST },
532 { "trailer-encapsulation", 34, 1 },
533 { "arp-timeout", 35, 4 | OT_DEC },
534 { "ethernet-encap", 36, 1 },
535 { "tcp-ttl", 37, 1 },
536 { "tcp-keepalive", 38, 4 | OT_DEC },
537 { "nis-domain", 40, OT_NAME },
538 { "nis-server", 41, OT_ADDR_LIST },
539 { "ntp-server", 42, OT_ADDR_LIST },
540 { "vendor-encap", 43, OT_INTERNAL },
541 { "netbios-ns", 44, OT_ADDR_LIST },
542 { "netbios-dd", 45, OT_ADDR_LIST },
543 { "netbios-nodetype", 46, 1 },
544 { "netbios-scope", 47, 0 },
545 { "x-windows-fs", 48, OT_ADDR_LIST },
546 { "x-windows-dm", 49, OT_ADDR_LIST },
547 { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100548 { "lease-time", 51, OT_INTERNAL | OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000549 { "option-overload", 52, OT_INTERNAL },
550 { "message-type", 53, OT_INTERNAL | OT_DEC },
551 { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
552 { "parameter-request", 55, OT_INTERNAL },
553 { "message", 56, OT_INTERNAL },
554 { "max-message-size", 57, OT_INTERNAL },
Simon Kelleyca85a282015-05-13 22:33:04 +0100555 { "T1", 58, OT_TIME},
556 { "T2", 59, OT_TIME},
Simon Kelley40ef23b2012-03-13 21:59:28 +0000557 { "vendor-class", 60, 0 },
558 { "client-id", 61, OT_INTERNAL },
559 { "nis+-domain", 64, OT_NAME },
560 { "nis+-server", 65, OT_ADDR_LIST },
561 { "tftp-server", 66, OT_NAME },
562 { "bootfile-name", 67, OT_NAME },
563 { "mobile-ip-home", 68, OT_ADDR_LIST },
564 { "smtp-server", 69, OT_ADDR_LIST },
565 { "pop3-server", 70, OT_ADDR_LIST },
566 { "nntp-server", 71, OT_ADDR_LIST },
567 { "irc-server", 74, OT_ADDR_LIST },
568 { "user-class", 77, 0 },
Simon Kelley734d5312018-03-23 23:09:53 +0000569 { "rapid-commit", 80, 0 },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000570 { "FQDN", 81, OT_INTERNAL },
571 { "agent-id", 82, OT_INTERNAL },
572 { "client-arch", 93, 2 | OT_DEC },
573 { "client-interface-id", 94, 0 },
574 { "client-machine-id", 97, 0 },
575 { "subnet-select", 118, OT_INTERNAL },
576 { "domain-search", 119, OT_RFC1035_NAME },
577 { "sip-server", 120, 0 },
578 { "classless-static-route", 121, 0 },
579 { "vendor-id-encap", 125, 0 },
Ville Skyttä0c211c42019-12-05 17:11:09 +0000580 { "tftp-server-address", 150, OT_ADDR_LIST },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000581 { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
582 { NULL, 0, 0 }
583};
584
585#ifdef HAVE_DHCP6
586static const struct opttab_t opttab6[] = {
587 { "client-id", 1, OT_INTERNAL },
588 { "server-id", 2, OT_INTERNAL },
589 { "ia-na", 3, OT_INTERNAL },
590 { "ia-ta", 4, OT_INTERNAL },
591 { "iaaddr", 5, OT_INTERNAL },
592 { "oro", 6, OT_INTERNAL },
593 { "preference", 7, OT_INTERNAL | OT_DEC },
594 { "unicast", 12, OT_INTERNAL },
595 { "status", 13, OT_INTERNAL },
596 { "rapid-commit", 14, OT_INTERNAL },
597 { "user-class", 15, OT_INTERNAL | OT_CSTRING },
598 { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
599 { "vendor-opts", 17, OT_INTERNAL },
600 { "sip-server-domain", 21, OT_RFC1035_NAME },
601 { "sip-server", 22, OT_ADDR_LIST },
602 { "dns-server", 23, OT_ADDR_LIST },
603 { "domain-search", 24, OT_RFC1035_NAME },
604 { "nis-server", 27, OT_ADDR_LIST },
605 { "nis+-server", 28, OT_ADDR_LIST },
606 { "nis-domain", 29, OT_RFC1035_NAME },
607 { "nis+-domain", 30, OT_RFC1035_NAME },
608 { "sntp-server", 31, OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100609 { "information-refresh-time", 32, OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000610 { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
Simon Kelley102208d2015-09-10 21:50:00 +0100611 { "ntp-server", 56, 0 },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000612 { "bootfile-url", 59, OT_NAME },
613 { "bootfile-param", 60, OT_CSTRING },
614 { NULL, 0, 0 }
615};
616#endif
617
618
619
620void display_opts(void)
621{
622 int i;
623
624 printf(_("Known DHCP options:\n"));
625
626 for (i = 0; opttab[i].name; i++)
627 if (!(opttab[i].size & OT_INTERNAL))
628 printf("%3d %s\n", opttab[i].val, opttab[i].name);
629}
630
631#ifdef HAVE_DHCP6
632void display_opts6(void)
633{
634 int i;
635 printf(_("Known DHCPv6 options:\n"));
636
637 for (i = 0; opttab6[i].name; i++)
638 if (!(opttab6[i].size & OT_INTERNAL))
639 printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
640}
641#endif
642
Simon Kelleybd08ae62013-04-19 10:22:06 +0100643int lookup_dhcp_opt(int prot, char *name)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000644{
645 const struct opttab_t *t;
646 int i;
647
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100648 (void)prot;
649
Simon Kelley40ef23b2012-03-13 21:59:28 +0000650#ifdef HAVE_DHCP6
651 if (prot == AF_INET6)
652 t = opttab6;
653 else
654#endif
655 t = opttab;
656
657 for (i = 0; t[i].name; i++)
Simon Kelleyc7961072013-02-28 15:17:58 +0000658 if (strcasecmp(t[i].name, name) == 0)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000659 return t[i].val;
660
Simon Kelleybd08ae62013-04-19 10:22:06 +0100661 return -1;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000662}
663
Simon Kelleybd08ae62013-04-19 10:22:06 +0100664int lookup_dhcp_len(int prot, int val)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000665{
666 const struct opttab_t *t;
667 int i;
668
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100669 (void)prot;
670
Simon Kelley40ef23b2012-03-13 21:59:28 +0000671#ifdef HAVE_DHCP6
672 if (prot == AF_INET6)
673 t = opttab6;
674 else
675#endif
676 t = opttab;
677
678 for (i = 0; t[i].name; i++)
679 if (val == t[i].val)
Simon Kelleyc7961072013-02-28 15:17:58 +0000680 return t[i].size & ~OT_DEC;
681
682 return 0;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000683}
684
685char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
686{
687 int o, i, j, nodecode = 0;
688 const struct opttab_t *ot = opttab;
689
690#ifdef HAVE_DHCP6
691 if (prot == AF_INET6)
692 ot = opttab6;
693#endif
694
695 for (o = 0; ot[o].name; o++)
696 if (ot[o].val == opt)
697 {
698 if (buf)
699 {
700 memset(buf, 0, buf_len);
701
702 if (ot[o].size & OT_ADDR_LIST)
703 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000704 union all_addr addr;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000705 int addr_len = INADDRSZ;
706
707#ifdef HAVE_DHCP6
708 if (prot == AF_INET6)
709 addr_len = IN6ADDRSZ;
710#endif
711 for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
712 {
713 if (i != 0)
714 strncat(buf, ", ", buf_len - strlen(buf));
715 /* align */
716 memcpy(&addr, &val[i], addr_len);
717 inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
718 strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
719 }
720 }
721 else if (ot[o].size & OT_NAME)
722 for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
723 {
724 char c = val[i];
725 if (isprint((int)c))
726 buf[j++] = c;
727 }
728#ifdef HAVE_DHCP6
729 /* We don't handle compressed rfc1035 names, so no good in IPv4 land */
730 else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
731 {
732 i = 0, j = 0;
733 while (i < opt_len && val[i] != 0)
734 {
735 int k, l = i + val[i] + 1;
736 for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
737 {
738 char c = val[k];
739 if (isprint((int)c))
740 buf[j++] = c;
741 }
742 i = l;
743 if (val[i] != 0 && j < buf_len)
744 buf[j++] = '.';
745 }
746 }
747 else if ((ot[o].size & OT_CSTRING))
748 {
749 int k, len;
750 unsigned char *p;
751
752 i = 0, j = 0;
753 while (1)
754 {
755 p = &val[i];
756 GETSHORT(len, p);
757 for (k = 0; k < len && j < buf_len; k++)
758 {
759 char c = *p++;
760 if (isprint((int)c))
761 buf[j++] = c;
762 }
763 i += len +2;
764 if (i >= opt_len)
765 break;
766
767 if (j < buf_len)
768 buf[j++] = ',';
769 }
770 }
771#endif
Simon Kelley23245c02012-07-18 16:21:11 +0100772 else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000773 {
774 unsigned int dec = 0;
775
776 for (i = 0; i < opt_len; i++)
777 dec = (dec << 8) | val[i];
778
Simon Kelley23245c02012-07-18 16:21:11 +0100779 if (ot[o].size & OT_TIME)
780 prettyprint_time(buf, dec);
781 else
782 sprintf(buf, "%u", dec);
Simon Kelley40ef23b2012-03-13 21:59:28 +0000783 }
784 else
785 nodecode = 1;
786 }
787 break;
788 }
789
790 if (opt_len != 0 && buf && (!ot[o].name || nodecode))
791 {
792 int trunc = 0;
793 if (opt_len > 14)
794 {
795 trunc = 1;
796 opt_len = 14;
797 }
798 print_mac(buf, val, opt_len);
799 if (trunc)
800 strncat(buf, "...", buf_len - strlen(buf));
801
802
803 }
804
805 return ot[o].name ? ot[o].name : "";
806
807}
808
Simon Kelley1f776932012-12-16 19:46:08 +0000809void log_context(int family, struct dhcp_context *context)
810{
811 /* Cannot use dhcp_buff* for RA contexts */
812
813 void *start = &context->start;
814 void *end = &context->end;
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000815 char *template = "", *p = daemon->namebuff;
Simon Kelleyc1be9172012-12-17 22:37:30 +0000816
817 *p = 0;
818
Simon Kelley1f776932012-12-16 19:46:08 +0000819#ifdef HAVE_DHCP6
820 if (family == AF_INET6)
821 {
822 struct in6_addr subnet = context->start6;
823 if (!(context->flags & CONTEXT_TEMPLATE))
824 setaddr6part(&subnet, 0);
825 inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN);
826 start = &context->start6;
827 end = &context->end6;
828 }
829#endif
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000830
831 if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
832 strcpy(daemon->namebuff, _(", prefix deprecated"));
833 else
Simon Kelley1f776932012-12-16 19:46:08 +0000834 {
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000835 p += sprintf(p, _(", lease time "));
836 prettyprint_time(p, context->lease_time);
837 p += strlen(p);
838 }
839
Simon Kelleyc1be9172012-12-17 22:37:30 +0000840#ifdef HAVE_DHCP6
841 if (context->flags & CONTEXT_CONSTRUCTED)
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000842 {
843 char ifrn_name[IFNAMSIZ];
844
845 template = p;
846 p += sprintf(p, ", ");
847
Simon Kelleya8105592013-09-25 15:36:00 +0100848 if (indextoname(daemon->icmp6fd, context->if_index, ifrn_name))
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100849 sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name);
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000850 }
Simon Kelley903650a2013-10-03 11:43:09 +0100851 else if (context->flags & CONTEXT_TEMPLATE && !(context->flags & CONTEXT_RA_STATELESS))
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000852 {
853 template = p;
854 p += sprintf(p, ", ");
Simon Kelley903650a2013-10-03 11:43:09 +0100855
Simon Kelley49333cb2013-03-15 20:30:51 +0000856 sprintf(p, "template for %s", context->template_interface);
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000857 }
Simon Kelleyc1be9172012-12-17 22:37:30 +0000858#endif
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000859
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100860 if (!(context->flags & CONTEXT_OLD) &&
861 ((context->flags & CONTEXT_DHCP) || family == AF_INET))
Simon Kelley1f776932012-12-16 19:46:08 +0000862 {
Simon Kelley903650a2013-10-03 11:43:09 +0100863#ifdef HAVE_DHCP6
864 if (context->flags & CONTEXT_RA_STATELESS)
865 {
866 if (context->flags & CONTEXT_TEMPLATE)
Simon Kelleybf4e62c2016-07-22 21:37:59 +0100867 strncpy(daemon->dhcp_buff, context->template_interface, DHCP_BUFF_SZ);
Simon Kelley903650a2013-10-03 11:43:09 +0100868 else
869 strcpy(daemon->dhcp_buff, daemon->addrbuff);
870 }
871 else
872#endif
Simon Kelleybf4e62c2016-07-22 21:37:59 +0100873 inet_ntop(family, start, daemon->dhcp_buff, DHCP_BUFF_SZ);
874 inet_ntop(family, end, daemon->dhcp_buff3, DHCP_BUFF_SZ);
Simon Kelley1f776932012-12-16 19:46:08 +0000875 my_syslog(MS_DHCP | LOG_INFO,
Simon Kelley903650a2013-10-03 11:43:09 +0100876 (context->flags & CONTEXT_RA_STATELESS) ?
877 _("%s stateless on %s%.0s%.0s%s") :
878 (context->flags & CONTEXT_STATIC) ?
879 _("%s, static leases only on %.0s%s%s%.0s") :
880 (context->flags & CONTEXT_PROXY) ?
881 _("%s, proxy on subnet %.0s%s%.0s%.0s") :
882 _("%s, IP range %s -- %s%s%.0s"),
883 (family != AF_INET) ? "DHCPv6" : "DHCP",
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000884 daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
Simon Kelley1f776932012-12-16 19:46:08 +0000885 }
886
Simon Kelleyc1be9172012-12-17 22:37:30 +0000887#ifdef HAVE_DHCP6
Simon Kelleye4e9b342013-10-02 11:00:45 +0100888 if (context->flags & CONTEXT_TEMPLATE)
889 {
890 strcpy(daemon->addrbuff, context->template_interface);
891 template = "";
892 }
Simon Kelley903650a2013-10-03 11:43:09 +0100893
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100894 if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000895 my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100896
Simon Kelleyb1a1b6d2013-01-11 16:28:50 +0000897 if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6))
898 my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
Simon Kelleyc1be9172012-12-17 22:37:30 +0000899#endif
900
Simon Kelley1f776932012-12-16 19:46:08 +0000901}
Simon Kelley1f776932012-12-16 19:46:08 +0000902
Simon Kelleyff7eea22013-09-04 18:01:38 +0100903void log_relay(int family, struct dhcp_relay *relay)
904{
905 inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
906 inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
907
908 if (relay->interface)
909 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
910 else
911 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
912}
913
Simon Kelley4cb1b322012-02-06 14:30:41 +0000914#endif