]>
Commit | Line | Data |
---|---|---|
61744359 | 1 | /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley |
4cb1b320 SK |
2 | |
3 | This program is free software; you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
5 | the Free Software Foundation; version 2 dated June, 1991, or | |
6 | (at your option) version 3 dated 29 June, 2007. | |
7 | ||
8 | This program is distributed in the hope that it will be useful, | |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
12 | ||
13 | You should have received a copy of the GNU General Public License | |
14 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include "dnsmasq.h" | |
18 | ||
19 | #ifdef HAVE_DHCP | |
20 | ||
21 | void dhcp_common_init(void) | |
22 | { | |
23 | /* These each hold a DHCP option max size 255 | |
24 | and get a terminating zero added */ | |
25 | daemon->dhcp_buff = safe_malloc(256); | |
26 | daemon->dhcp_buff2 = safe_malloc(256); | |
27 | daemon->dhcp_buff3 = safe_malloc(256); | |
28 | ||
29 | /* dhcp_packet is used by v4 and v6, outpacket only by v6 | |
30 | sizeof(struct dhcp_packet) is as good an initial size as any, | |
31 | even for v6 */ | |
32 | 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 | ||
39 | ssize_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 */ | |
c5ad4e79 | 56 | if ((size_t)sz == msg->msg_iov->iov_len) |
4cb1b320 | 57 | { |
c5ad4e79 | 58 | if (!expand_buf(msg->msg_iov, sz + 100)) |
4cb1b320 SK |
59 | return -1; |
60 | } | |
61 | else | |
62 | { | |
c5ad4e79 | 63 | expand_buf(msg->msg_iov, sz); |
4cb1b320 SK |
64 | 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 | ||
73 | struct 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 | ||
90 | struct 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; | |
96c727fd | 94 | struct dhcp_opt *tmp; |
4cb1b320 SK |
95 | |
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, | |
6caacacf | 106 | otherwise valid options are inhibited if we found a higher priority one above */ |
4cb1b320 SK |
107 | 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 | ||
a8131113 SK |
115 | /* 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 | ||
4cb1b320 SK |
122 | 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 | { | |
4cb1b320 SK |
139 | 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 | ||
96c727fd SK |
148 | /* 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 | ||
4cb1b320 SK |
155 | return tagif; |
156 | } | |
157 | ||
158 | /* Is every member of check matched by a member of pool? | |
159 | If tagnotneeded, untagged is OK */ | |
160 | int 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. */ | |
187 | char *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 | ||
201 | void 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 | ||
3634c54e SK |
226 | int 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 | } | |
ceae00dd | 255 | |
ceae00dd SK |
256 | void dhcp_update_configs(struct dhcp_config *configs) |
257 | { | |
258 | /* Some people like to keep all static IP addresses in /etc/hosts. | |
259 | This goes through /etc/hosts and sets static addresses for any DHCP config | |
260 | records which don't have an address and whose name matches. | |
261 | We take care to maintain the invariant that any IP address can appear | |
262 | in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, | |
263 | restore the status-quo ante first. */ | |
264 | ||
35239a30 | 265 | struct dhcp_config *config, *conf_tmp; |
ceae00dd SK |
266 | struct crec *crec; |
267 | int prot = AF_INET; | |
268 | ||
269 | for (config = configs; config; config = config->next) | |
270 | if (config->flags & CONFIG_ADDR_HOSTS) | |
271 | config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS); | |
272 | ||
273 | #ifdef HAVE_DHCP6 | |
274 | again: | |
275 | #endif | |
276 | ||
277 | if (daemon->port != 0) | |
278 | for (config = configs; config; config = config->next) | |
279 | { | |
280 | int conflags = CONFIG_ADDR; | |
281 | int cacheflags = F_IPV4; | |
282 | ||
283 | #ifdef HAVE_DHCP6 | |
284 | if (prot == AF_INET6) | |
285 | { | |
286 | conflags = CONFIG_ADDR6; | |
287 | cacheflags = F_IPV6; | |
288 | } | |
289 | #endif | |
290 | if (!(config->flags & conflags) && | |
291 | (config->flags & CONFIG_NAME) && | |
292 | (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) && | |
293 | (crec->flags & F_HOSTS)) | |
294 | { | |
295 | if (cache_find_by_name(crec, config->hostname, 0, cacheflags)) | |
296 | { | |
297 | /* use primary (first) address */ | |
298 | while (crec && !(crec->flags & F_REVERSE)) | |
299 | crec = cache_find_by_name(crec, config->hostname, 0, cacheflags); | |
300 | if (!crec) | |
301 | continue; /* should be never */ | |
302 | inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); | |
303 | my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), | |
304 | config->hostname, daemon->addrbuff); | |
305 | } | |
306 | ||
35239a30 SK |
307 | if (prot == AF_INET && |
308 | (!(conf_tmp = config_find_by_address(configs, crec->addr.addr.addr.addr4)) || conf_tmp == config)) | |
ceae00dd SK |
309 | { |
310 | config->addr = crec->addr.addr.addr.addr4; | |
311 | config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS; | |
312 | continue; | |
313 | } | |
314 | ||
315 | #ifdef HAVE_DHCP6 | |
35239a30 SK |
316 | if (prot == AF_INET6 && |
317 | (!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) || conf_tmp == config)) | |
ceae00dd | 318 | { |
e44ddcac | 319 | memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ); |
ceae00dd SK |
320 | config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS; |
321 | continue; | |
322 | } | |
323 | #endif | |
324 | ||
325 | inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); | |
326 | my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), | |
327 | daemon->addrbuff, config->hostname); | |
328 | ||
329 | ||
330 | } | |
331 | } | |
332 | ||
333 | #ifdef HAVE_DHCP6 | |
334 | if (prot == AF_INET) | |
335 | { | |
336 | prot = AF_INET6; | |
337 | goto again; | |
338 | } | |
339 | #endif | |
340 | ||
341 | } | |
4cb1b320 | 342 | |
9380ba70 SK |
343 | #ifdef HAVE_LINUX_NETWORK |
344 | void bindtodevice(int fd) | |
345 | { | |
346 | /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE | |
347 | to that device. This is for the use case of (eg) OpenStack, which runs a new | |
348 | dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE, | |
349 | individual processes don't always see the packets they should. | |
e2ba0df2 SK |
350 | SO_BINDTODEVICE is only available Linux. |
351 | ||
352 | Note that if wildcards are used in --interface, or a configured interface doesn't | |
353 | yet exist, then more interfaces may arrive later, so we can't safely assert there | |
354 | is only one interface and proceed. | |
355 | */ | |
9380ba70 SK |
356 | |
357 | struct irec *iface, *found; | |
e2ba0df2 SK |
358 | struct iname *if_tmp; |
359 | ||
360 | for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) | |
361 | if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*'))) | |
362 | return; | |
363 | ||
9380ba70 SK |
364 | for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next) |
365 | if (iface->dhcp_ok) | |
366 | { | |
367 | if (!found) | |
368 | found = iface; | |
369 | else if (strcmp(found->name, iface->name) != 0) | |
e2ba0df2 | 370 | return; /* more than one. */ |
9380ba70 SK |
371 | } |
372 | ||
373 | if (found) | |
b191a779 SK |
374 | { |
375 | struct ifreq ifr; | |
376 | strcpy(ifr.ifr_name, found->name); | |
377 | /* only allowed by root. */ | |
378 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 && | |
379 | errno != EPERM) | |
380 | die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET); | |
381 | } | |
9380ba70 SK |
382 | } |
383 | #endif | |
40ef23b5 SK |
384 | |
385 | static const struct opttab_t { | |
386 | char *name; | |
387 | u16 val, size; | |
388 | } opttab[] = { | |
389 | { "netmask", 1, OT_ADDR_LIST }, | |
390 | { "time-offset", 2, 4 }, | |
391 | { "router", 3, OT_ADDR_LIST }, | |
392 | { "dns-server", 6, OT_ADDR_LIST }, | |
393 | { "log-server", 7, OT_ADDR_LIST }, | |
394 | { "lpr-server", 9, OT_ADDR_LIST }, | |
395 | { "hostname", 12, OT_INTERNAL | OT_NAME }, | |
396 | { "boot-file-size", 13, 2 | OT_DEC }, | |
397 | { "domain-name", 15, OT_NAME }, | |
398 | { "swap-server", 16, OT_ADDR_LIST }, | |
399 | { "root-path", 17, OT_NAME }, | |
400 | { "extension-path", 18, OT_NAME }, | |
401 | { "ip-forward-enable", 19, 1 }, | |
402 | { "non-local-source-routing", 20, 1 }, | |
403 | { "policy-filter", 21, OT_ADDR_LIST }, | |
404 | { "max-datagram-reassembly", 22, 2 | OT_DEC }, | |
405 | { "default-ttl", 23, 1 | OT_DEC }, | |
406 | { "mtu", 26, 2 | OT_DEC }, | |
407 | { "all-subnets-local", 27, 1 }, | |
408 | { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST }, | |
409 | { "router-discovery", 31, 1 }, | |
410 | { "router-solicitation", 32, OT_ADDR_LIST }, | |
411 | { "static-route", 33, OT_ADDR_LIST }, | |
412 | { "trailer-encapsulation", 34, 1 }, | |
413 | { "arp-timeout", 35, 4 | OT_DEC }, | |
414 | { "ethernet-encap", 36, 1 }, | |
415 | { "tcp-ttl", 37, 1 }, | |
416 | { "tcp-keepalive", 38, 4 | OT_DEC }, | |
417 | { "nis-domain", 40, OT_NAME }, | |
418 | { "nis-server", 41, OT_ADDR_LIST }, | |
419 | { "ntp-server", 42, OT_ADDR_LIST }, | |
420 | { "vendor-encap", 43, OT_INTERNAL }, | |
421 | { "netbios-ns", 44, OT_ADDR_LIST }, | |
422 | { "netbios-dd", 45, OT_ADDR_LIST }, | |
423 | { "netbios-nodetype", 46, 1 }, | |
424 | { "netbios-scope", 47, 0 }, | |
425 | { "x-windows-fs", 48, OT_ADDR_LIST }, | |
426 | { "x-windows-dm", 49, OT_ADDR_LIST }, | |
427 | { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST }, | |
23245c0c | 428 | { "lease-time", 51, OT_INTERNAL | OT_TIME }, |
40ef23b5 SK |
429 | { "option-overload", 52, OT_INTERNAL }, |
430 | { "message-type", 53, OT_INTERNAL | OT_DEC }, | |
431 | { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST }, | |
432 | { "parameter-request", 55, OT_INTERNAL }, | |
433 | { "message", 56, OT_INTERNAL }, | |
434 | { "max-message-size", 57, OT_INTERNAL }, | |
23245c0c SK |
435 | { "T1", 58, OT_INTERNAL | OT_TIME}, |
436 | { "T2", 59, OT_INTERNAL | OT_TIME}, | |
40ef23b5 SK |
437 | { "vendor-class", 60, 0 }, |
438 | { "client-id", 61, OT_INTERNAL }, | |
439 | { "nis+-domain", 64, OT_NAME }, | |
440 | { "nis+-server", 65, OT_ADDR_LIST }, | |
441 | { "tftp-server", 66, OT_NAME }, | |
442 | { "bootfile-name", 67, OT_NAME }, | |
443 | { "mobile-ip-home", 68, OT_ADDR_LIST }, | |
444 | { "smtp-server", 69, OT_ADDR_LIST }, | |
445 | { "pop3-server", 70, OT_ADDR_LIST }, | |
446 | { "nntp-server", 71, OT_ADDR_LIST }, | |
447 | { "irc-server", 74, OT_ADDR_LIST }, | |
448 | { "user-class", 77, 0 }, | |
449 | { "FQDN", 81, OT_INTERNAL }, | |
450 | { "agent-id", 82, OT_INTERNAL }, | |
451 | { "client-arch", 93, 2 | OT_DEC }, | |
452 | { "client-interface-id", 94, 0 }, | |
453 | { "client-machine-id", 97, 0 }, | |
454 | { "subnet-select", 118, OT_INTERNAL }, | |
455 | { "domain-search", 119, OT_RFC1035_NAME }, | |
456 | { "sip-server", 120, 0 }, | |
457 | { "classless-static-route", 121, 0 }, | |
458 | { "vendor-id-encap", 125, 0 }, | |
459 | { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */ | |
460 | { NULL, 0, 0 } | |
461 | }; | |
462 | ||
463 | #ifdef HAVE_DHCP6 | |
464 | static const struct opttab_t opttab6[] = { | |
465 | { "client-id", 1, OT_INTERNAL }, | |
466 | { "server-id", 2, OT_INTERNAL }, | |
467 | { "ia-na", 3, OT_INTERNAL }, | |
468 | { "ia-ta", 4, OT_INTERNAL }, | |
469 | { "iaaddr", 5, OT_INTERNAL }, | |
470 | { "oro", 6, OT_INTERNAL }, | |
471 | { "preference", 7, OT_INTERNAL | OT_DEC }, | |
472 | { "unicast", 12, OT_INTERNAL }, | |
473 | { "status", 13, OT_INTERNAL }, | |
474 | { "rapid-commit", 14, OT_INTERNAL }, | |
475 | { "user-class", 15, OT_INTERNAL | OT_CSTRING }, | |
476 | { "vendor-class", 16, OT_INTERNAL | OT_CSTRING }, | |
477 | { "vendor-opts", 17, OT_INTERNAL }, | |
478 | { "sip-server-domain", 21, OT_RFC1035_NAME }, | |
479 | { "sip-server", 22, OT_ADDR_LIST }, | |
480 | { "dns-server", 23, OT_ADDR_LIST }, | |
481 | { "domain-search", 24, OT_RFC1035_NAME }, | |
482 | { "nis-server", 27, OT_ADDR_LIST }, | |
483 | { "nis+-server", 28, OT_ADDR_LIST }, | |
484 | { "nis-domain", 29, OT_RFC1035_NAME }, | |
485 | { "nis+-domain", 30, OT_RFC1035_NAME }, | |
486 | { "sntp-server", 31, OT_ADDR_LIST }, | |
23245c0c | 487 | { "information-refresh-time", 32, OT_TIME }, |
40ef23b5 SK |
488 | { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME }, |
489 | { "ntp-server", 56, OT_ADDR_LIST }, | |
490 | { "bootfile-url", 59, OT_NAME }, | |
491 | { "bootfile-param", 60, OT_CSTRING }, | |
492 | { NULL, 0, 0 } | |
493 | }; | |
494 | #endif | |
495 | ||
496 | ||
497 | ||
498 | void display_opts(void) | |
499 | { | |
500 | int i; | |
501 | ||
502 | printf(_("Known DHCP options:\n")); | |
503 | ||
504 | for (i = 0; opttab[i].name; i++) | |
505 | if (!(opttab[i].size & OT_INTERNAL)) | |
506 | printf("%3d %s\n", opttab[i].val, opttab[i].name); | |
507 | } | |
508 | ||
509 | #ifdef HAVE_DHCP6 | |
510 | void display_opts6(void) | |
511 | { | |
512 | int i; | |
513 | printf(_("Known DHCPv6 options:\n")); | |
514 | ||
515 | for (i = 0; opttab6[i].name; i++) | |
516 | if (!(opttab6[i].size & OT_INTERNAL)) | |
517 | printf("%3d %s\n", opttab6[i].val, opttab6[i].name); | |
518 | } | |
519 | #endif | |
520 | ||
bd08ae67 | 521 | int lookup_dhcp_opt(int prot, char *name) |
40ef23b5 SK |
522 | { |
523 | const struct opttab_t *t; | |
524 | int i; | |
525 | ||
526 | #ifdef HAVE_DHCP6 | |
527 | if (prot == AF_INET6) | |
528 | t = opttab6; | |
529 | else | |
530 | #endif | |
531 | t = opttab; | |
532 | ||
533 | for (i = 0; t[i].name; i++) | |
c7961075 | 534 | if (strcasecmp(t[i].name, name) == 0) |
40ef23b5 SK |
535 | return t[i].val; |
536 | ||
bd08ae67 | 537 | return -1; |
40ef23b5 SK |
538 | } |
539 | ||
bd08ae67 | 540 | int lookup_dhcp_len(int prot, int val) |
40ef23b5 SK |
541 | { |
542 | const struct opttab_t *t; | |
543 | int i; | |
544 | ||
545 | #ifdef HAVE_DHCP6 | |
546 | if (prot == AF_INET6) | |
547 | t = opttab6; | |
548 | else | |
549 | #endif | |
550 | t = opttab; | |
551 | ||
552 | for (i = 0; t[i].name; i++) | |
553 | if (val == t[i].val) | |
c7961075 SK |
554 | return t[i].size & ~OT_DEC; |
555 | ||
556 | return 0; | |
40ef23b5 SK |
557 | } |
558 | ||
559 | char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len) | |
560 | { | |
561 | int o, i, j, nodecode = 0; | |
562 | const struct opttab_t *ot = opttab; | |
563 | ||
564 | #ifdef HAVE_DHCP6 | |
565 | if (prot == AF_INET6) | |
566 | ot = opttab6; | |
567 | #endif | |
568 | ||
569 | for (o = 0; ot[o].name; o++) | |
570 | if (ot[o].val == opt) | |
571 | { | |
572 | if (buf) | |
573 | { | |
574 | memset(buf, 0, buf_len); | |
575 | ||
576 | if (ot[o].size & OT_ADDR_LIST) | |
577 | { | |
578 | struct all_addr addr; | |
579 | int addr_len = INADDRSZ; | |
580 | ||
581 | #ifdef HAVE_DHCP6 | |
582 | if (prot == AF_INET6) | |
583 | addr_len = IN6ADDRSZ; | |
584 | #endif | |
585 | for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len) | |
586 | { | |
587 | if (i != 0) | |
588 | strncat(buf, ", ", buf_len - strlen(buf)); | |
589 | /* align */ | |
590 | memcpy(&addr, &val[i], addr_len); | |
591 | inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN); | |
592 | strncat(buf, daemon->addrbuff, buf_len - strlen(buf)); | |
593 | } | |
594 | } | |
595 | else if (ot[o].size & OT_NAME) | |
596 | for (i = 0, j = 0; i < opt_len && j < buf_len ; i++) | |
597 | { | |
598 | char c = val[i]; | |
599 | if (isprint((int)c)) | |
600 | buf[j++] = c; | |
601 | } | |
602 | #ifdef HAVE_DHCP6 | |
603 | /* We don't handle compressed rfc1035 names, so no good in IPv4 land */ | |
604 | else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6) | |
605 | { | |
606 | i = 0, j = 0; | |
607 | while (i < opt_len && val[i] != 0) | |
608 | { | |
609 | int k, l = i + val[i] + 1; | |
610 | for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++) | |
611 | { | |
612 | char c = val[k]; | |
613 | if (isprint((int)c)) | |
614 | buf[j++] = c; | |
615 | } | |
616 | i = l; | |
617 | if (val[i] != 0 && j < buf_len) | |
618 | buf[j++] = '.'; | |
619 | } | |
620 | } | |
621 | else if ((ot[o].size & OT_CSTRING)) | |
622 | { | |
623 | int k, len; | |
624 | unsigned char *p; | |
625 | ||
626 | i = 0, j = 0; | |
627 | while (1) | |
628 | { | |
629 | p = &val[i]; | |
630 | GETSHORT(len, p); | |
631 | for (k = 0; k < len && j < buf_len; k++) | |
632 | { | |
633 | char c = *p++; | |
634 | if (isprint((int)c)) | |
635 | buf[j++] = c; | |
636 | } | |
637 | i += len +2; | |
638 | if (i >= opt_len) | |
639 | break; | |
640 | ||
641 | if (j < buf_len) | |
642 | buf[j++] = ','; | |
643 | } | |
644 | } | |
645 | #endif | |
23245c0c | 646 | else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0) |
40ef23b5 SK |
647 | { |
648 | unsigned int dec = 0; | |
649 | ||
650 | for (i = 0; i < opt_len; i++) | |
651 | dec = (dec << 8) | val[i]; | |
652 | ||
23245c0c SK |
653 | if (ot[o].size & OT_TIME) |
654 | prettyprint_time(buf, dec); | |
655 | else | |
656 | sprintf(buf, "%u", dec); | |
40ef23b5 SK |
657 | } |
658 | else | |
659 | nodecode = 1; | |
660 | } | |
661 | break; | |
662 | } | |
663 | ||
664 | if (opt_len != 0 && buf && (!ot[o].name || nodecode)) | |
665 | { | |
666 | int trunc = 0; | |
667 | if (opt_len > 14) | |
668 | { | |
669 | trunc = 1; | |
670 | opt_len = 14; | |
671 | } | |
672 | print_mac(buf, val, opt_len); | |
673 | if (trunc) | |
674 | strncat(buf, "...", buf_len - strlen(buf)); | |
675 | ||
676 | ||
677 | } | |
678 | ||
679 | return ot[o].name ? ot[o].name : ""; | |
680 | ||
681 | } | |
682 | ||
1f776932 SK |
683 | void log_context(int family, struct dhcp_context *context) |
684 | { | |
685 | /* Cannot use dhcp_buff* for RA contexts */ | |
686 | ||
687 | void *start = &context->start; | |
688 | void *end = &context->end; | |
b1a1b6de | 689 | char *template = "", *p = daemon->namebuff; |
c1be9177 SK |
690 | |
691 | *p = 0; | |
692 | ||
1f776932 SK |
693 | #ifdef HAVE_DHCP6 |
694 | if (family == AF_INET6) | |
695 | { | |
696 | struct in6_addr subnet = context->start6; | |
697 | if (!(context->flags & CONTEXT_TEMPLATE)) | |
698 | setaddr6part(&subnet, 0); | |
699 | inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN); | |
700 | start = &context->start6; | |
701 | end = &context->end6; | |
702 | } | |
703 | #endif | |
b1a1b6de SK |
704 | |
705 | if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE)) | |
706 | strcpy(daemon->namebuff, _(", prefix deprecated")); | |
707 | else | |
1f776932 | 708 | { |
b1a1b6de SK |
709 | p += sprintf(p, _(", lease time ")); |
710 | prettyprint_time(p, context->lease_time); | |
711 | p += strlen(p); | |
712 | } | |
713 | ||
c1be9177 SK |
714 | #ifdef HAVE_DHCP6 |
715 | if (context->flags & CONTEXT_CONSTRUCTED) | |
b1a1b6de SK |
716 | { |
717 | char ifrn_name[IFNAMSIZ]; | |
718 | ||
719 | template = p; | |
720 | p += sprintf(p, ", "); | |
721 | ||
722 | if (indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, context->if_index, ifrn_name)) | |
723 | sprintf(p, "constructed for %s", ifrn_name); | |
724 | } | |
725 | else if (context->flags & CONTEXT_TEMPLATE) | |
726 | { | |
727 | template = p; | |
728 | p += sprintf(p, ", "); | |
729 | ||
49333cbd | 730 | sprintf(p, "template for %s", context->template_interface); |
b1a1b6de | 731 | } |
c1be9177 | 732 | #endif |
b1a1b6de | 733 | |
1f776932 SK |
734 | if ((context->flags & CONTEXT_DHCP) || family == AF_INET) |
735 | { | |
736 | inet_ntop(family, start, daemon->dhcp_buff, 256); | |
737 | inet_ntop(family, end, daemon->dhcp_buff3, 256); | |
738 | my_syslog(MS_DHCP | LOG_INFO, | |
739 | (context->flags & CONTEXT_RA_STATELESS) ? | |
b1a1b6de | 740 | _("%s stateless on %s%.0s%.0s%s") : |
1f776932 | 741 | (context->flags & CONTEXT_STATIC) ? |
b1a1b6de | 742 | _("%s, static leases only on %.0s%s%s%.0s") : |
1f776932 | 743 | (context->flags & CONTEXT_PROXY) ? |
b1a1b6de SK |
744 | _("%s, proxy on subnet %.0s%s%.0s%.0s") : |
745 | _("%s, IP range %s -- %s%s%.0s"), | |
1f776932 | 746 | (family != AF_INET) ? "DHCPv6" : "DHCP", |
b1a1b6de | 747 | daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template); |
1f776932 SK |
748 | } |
749 | ||
c1be9177 | 750 | #ifdef HAVE_DHCP6 |
1f776932 | 751 | if (context->flags & CONTEXT_RA_NAME) |
b1a1b6de | 752 | my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template); |
1f776932 | 753 | |
b1a1b6de SK |
754 | if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6)) |
755 | my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template); | |
c1be9177 SK |
756 | #endif |
757 | ||
1f776932 SK |
758 | } |
759 | ||
760 | ||
4cb1b320 | 761 | #endif |