]>
Commit | Line | Data |
---|---|---|
4cb1b320 SK |
1 | /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley |
2 | ||
3 | This program is free software; you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
5 | the Free Software Foundation; version 2 dated June, 1991, or | |
6 | (at your option) version 3 dated 29 June, 2007. | |
7 | ||
8 | This program is distributed in the hope that it will be useful, | |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
12 | ||
13 | You should have received a copy of the GNU General Public License | |
14 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include "dnsmasq.h" | |
18 | ||
19 | #ifdef HAVE_DHCP | |
20 | ||
21 | void dhcp_common_init(void) | |
22 | { | |
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; | |
94 | ||
95 | /* flag options which are valid with the current tag set (sans context tags) */ | |
96 | for (opt = opts; opt; opt = opt->next) | |
97 | { | |
98 | opt->flags &= ~DHOPT_TAGOK; | |
99 | if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) && | |
100 | match_netid(opt->netid, tagif, 0)) | |
101 | opt->flags |= DHOPT_TAGOK; | |
102 | } | |
103 | ||
104 | /* now flag options which are valid, including the context tags, | |
6caacacf | 105 | otherwise valid options are inhibited if we found a higher priority one above */ |
4cb1b320 SK |
106 | if (context_tags) |
107 | { | |
108 | struct dhcp_netid *last_tag; | |
109 | ||
110 | for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next); | |
111 | last_tag->next = tags; | |
112 | tagif = run_tag_if(context_tags); | |
113 | ||
114 | for (opt = opts; opt; opt = opt->next) | |
115 | if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && | |
116 | match_netid(opt->netid, tagif, 0)) | |
117 | { | |
118 | struct dhcp_opt *tmp; | |
119 | for (tmp = opts; tmp; tmp = tmp->next) | |
120 | if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK)) | |
121 | break; | |
122 | if (!tmp) | |
123 | opt->flags |= DHOPT_TAGOK; | |
124 | } | |
125 | } | |
126 | ||
127 | /* now flag untagged options which are not overridden by tagged ones */ | |
128 | for (opt = opts; opt; opt = opt->next) | |
129 | if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid) | |
130 | { | |
131 | struct dhcp_opt *tmp; | |
132 | for (tmp = opts; tmp; tmp = tmp->next) | |
133 | if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK)) | |
134 | break; | |
135 | if (!tmp) | |
136 | opt->flags |= DHOPT_TAGOK; | |
137 | else if (!tmp->netid) | |
138 | my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); | |
139 | } | |
140 | ||
141 | return tagif; | |
142 | } | |
143 | ||
144 | /* Is every member of check matched by a member of pool? | |
145 | If tagnotneeded, untagged is OK */ | |
146 | int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded) | |
147 | { | |
148 | struct dhcp_netid *tmp1; | |
149 | ||
150 | if (!check && !tagnotneeded) | |
151 | return 0; | |
152 | ||
153 | for (; check; check = check->next) | |
154 | { | |
155 | /* '#' for not is for backwards compat. */ | |
156 | if (check->net[0] != '!' && check->net[0] != '#') | |
157 | { | |
158 | for (tmp1 = pool; tmp1; tmp1 = tmp1->next) | |
159 | if (strcmp(check->net, tmp1->net) == 0) | |
160 | break; | |
161 | if (!tmp1) | |
162 | return 0; | |
163 | } | |
164 | else | |
165 | for (tmp1 = pool; tmp1; tmp1 = tmp1->next) | |
166 | if (strcmp((check->net)+1, tmp1->net) == 0) | |
167 | return 0; | |
168 | } | |
169 | return 1; | |
170 | } | |
171 | ||
172 | /* return domain or NULL if none. */ | |
173 | char *strip_hostname(char *hostname) | |
174 | { | |
175 | char *dot = strchr(hostname, '.'); | |
176 | ||
177 | if (!dot) | |
178 | return NULL; | |
179 | ||
180 | *dot = 0; /* truncate */ | |
181 | if (strlen(dot+1) != 0) | |
182 | return dot+1; | |
183 | ||
184 | return NULL; | |
185 | } | |
186 | ||
187 | void log_tags(struct dhcp_netid *netid, u32 xid) | |
188 | { | |
189 | if (netid && option_bool(OPT_LOG_OPTS)) | |
190 | { | |
191 | char *s = daemon->namebuff; | |
192 | for (*s = 0; netid; netid = netid->next) | |
193 | { | |
194 | /* kill dupes. */ | |
195 | struct dhcp_netid *n; | |
196 | ||
197 | for (n = netid->next; n; n = n->next) | |
198 | if (strcmp(netid->net, n->net) == 0) | |
199 | break; | |
200 | ||
201 | if (!n) | |
202 | { | |
203 | strncat (s, netid->net, (MAXDNAME-1) - strlen(s)); | |
204 | if (netid->next) | |
205 | strncat (s, ", ", (MAXDNAME-1) - strlen(s)); | |
206 | } | |
207 | } | |
208 | my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s); | |
209 | } | |
210 | } | |
211 | ||
3634c54e SK |
212 | int match_bytes(struct dhcp_opt *o, unsigned char *p, int len) |
213 | { | |
214 | int i; | |
215 | ||
216 | if (o->len > len) | |
217 | return 0; | |
218 | ||
219 | if (o->len == 0) | |
220 | return 1; | |
221 | ||
222 | if (o->flags & DHOPT_HEX) | |
223 | { | |
224 | if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask)) | |
225 | return 1; | |
226 | } | |
227 | else | |
228 | for (i = 0; i <= (len - o->len); ) | |
229 | { | |
230 | if (memcmp(o->val, p + i, o->len) == 0) | |
231 | return 1; | |
232 | ||
233 | if (o->flags & DHOPT_STRING) | |
234 | i++; | |
235 | else | |
236 | i += o->len; | |
237 | } | |
238 | ||
239 | return 0; | |
240 | } | |
ceae00dd SK |
241 | |
242 | void check_dhcp_hosts(int fatal) | |
243 | { | |
244 | /* If the same IP appears in more than one host config, then DISCOVER | |
245 | for one of the hosts will get the address, but REQUEST will be NAKed, | |
246 | since the address is reserved by the other one -> protocol loop. | |
247 | Also check that FQDNs match the domain we are using. */ | |
248 | ||
249 | struct dhcp_config *configs, *cp; | |
250 | ||
251 | for (configs = daemon->dhcp_conf; configs; configs = configs->next) | |
252 | { | |
253 | char *domain; | |
254 | ||
255 | if ((configs->flags & DHOPT_BANK) || fatal) | |
256 | { | |
257 | for (cp = configs->next; cp; cp = cp->next) | |
258 | if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr) | |
259 | { | |
260 | if (fatal) | |
261 | die(_("duplicate IP address %s in dhcp-config directive."), | |
262 | inet_ntoa(cp->addr), EC_BADCONF); | |
263 | else | |
264 | my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."), | |
265 | inet_ntoa(cp->addr), daemon->dhcp_hosts_file); | |
266 | configs->flags &= ~CONFIG_ADDR; | |
267 | } | |
268 | ||
269 | /* split off domain part */ | |
270 | if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname))) | |
271 | configs->domain = domain; | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | void dhcp_update_configs(struct dhcp_config *configs) | |
277 | { | |
278 | /* Some people like to keep all static IP addresses in /etc/hosts. | |
279 | This goes through /etc/hosts and sets static addresses for any DHCP config | |
280 | records which don't have an address and whose name matches. | |
281 | We take care to maintain the invariant that any IP address can appear | |
282 | in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, | |
283 | restore the status-quo ante first. */ | |
284 | ||
285 | struct dhcp_config *config; | |
286 | struct crec *crec; | |
287 | int prot = AF_INET; | |
288 | ||
289 | for (config = configs; config; config = config->next) | |
290 | if (config->flags & CONFIG_ADDR_HOSTS) | |
291 | config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS); | |
292 | ||
293 | #ifdef HAVE_DHCP6 | |
294 | again: | |
295 | #endif | |
296 | ||
297 | if (daemon->port != 0) | |
298 | for (config = configs; config; config = config->next) | |
299 | { | |
300 | int conflags = CONFIG_ADDR; | |
301 | int cacheflags = F_IPV4; | |
302 | ||
303 | #ifdef HAVE_DHCP6 | |
304 | if (prot == AF_INET6) | |
305 | { | |
306 | conflags = CONFIG_ADDR6; | |
307 | cacheflags = F_IPV6; | |
308 | } | |
309 | #endif | |
310 | if (!(config->flags & conflags) && | |
311 | (config->flags & CONFIG_NAME) && | |
312 | (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) && | |
313 | (crec->flags & F_HOSTS)) | |
314 | { | |
315 | if (cache_find_by_name(crec, config->hostname, 0, cacheflags)) | |
316 | { | |
317 | /* use primary (first) address */ | |
318 | while (crec && !(crec->flags & F_REVERSE)) | |
319 | crec = cache_find_by_name(crec, config->hostname, 0, cacheflags); | |
320 | if (!crec) | |
321 | continue; /* should be never */ | |
322 | inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); | |
323 | my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), | |
324 | config->hostname, daemon->addrbuff); | |
325 | } | |
326 | ||
327 | if (prot == AF_INET && !config_find_by_address(configs, crec->addr.addr.addr.addr4)) | |
328 | { | |
329 | config->addr = crec->addr.addr.addr.addr4; | |
330 | config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS; | |
331 | continue; | |
332 | } | |
333 | ||
334 | #ifdef HAVE_DHCP6 | |
e44ddcac | 335 | if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) |
ceae00dd | 336 | { |
e44ddcac | 337 | memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ); |
ceae00dd SK |
338 | config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS; |
339 | continue; | |
340 | } | |
341 | #endif | |
342 | ||
343 | inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); | |
344 | my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), | |
345 | daemon->addrbuff, config->hostname); | |
346 | ||
347 | ||
348 | } | |
349 | } | |
350 | ||
351 | #ifdef HAVE_DHCP6 | |
352 | if (prot == AF_INET) | |
353 | { | |
354 | prot = AF_INET6; | |
355 | goto again; | |
356 | } | |
357 | #endif | |
358 | ||
359 | } | |
4cb1b320 | 360 | |
843c96b4 SK |
361 | #ifdef HAVE_DHCP6 |
362 | static int join_multicast_worker(struct in6_addr *local, int prefix, | |
363 | int scope, int if_index, int dad, void *vparam) | |
364 | { | |
365 | char ifrn_name[IFNAMSIZ]; | |
366 | struct ipv6_mreq mreq; | |
367 | int fd, i, max = *((int *)vparam); | |
368 | struct dhcp_context *context; | |
369 | struct iname *tmp; | |
370 | ||
371 | (void)prefix; | |
372 | (void)scope; | |
373 | (void)dad; | |
374 | ||
375 | /* record which interfaces we join on, so that we do it at most one per | |
376 | interface, even when they have multiple addresses. Use outpacket | |
377 | as an array of int, since it's always allocated here and easy | |
378 | to expand for theoretical vast numbers of interfaces. */ | |
379 | for (i = 0; i < max; i++) | |
380 | if (if_index == ((int *)daemon->outpacket.iov_base)[i]) | |
381 | return 1; | |
382 | ||
383 | if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) | |
384 | return 0; | |
385 | ||
386 | if (!indextoname(fd, if_index, ifrn_name)) | |
387 | { | |
388 | close(fd); | |
389 | return 0; | |
390 | } | |
391 | ||
392 | close(fd); | |
393 | ||
394 | /* Are we doing DHCP on this interface? */ | |
395 | if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name)) | |
396 | return 1; | |
397 | ||
398 | for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) | |
399 | if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0)) | |
400 | return 1; | |
401 | ||
402 | /* weird libvirt-inspired access control */ | |
403 | for (context = daemon->dhcp6; context; context = context->next) | |
404 | if (!context->interface || strcmp(context->interface, ifrn_name) == 0) | |
405 | break; | |
406 | ||
407 | if (!context) | |
408 | return 1; | |
409 | ||
410 | mreq.ipv6mr_interface = if_index; | |
411 | ||
412 | inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr); | |
413 | ||
414 | if (daemon->dhcp6 && | |
415 | setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) | |
416 | return 0; | |
417 | ||
418 | inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr); | |
419 | ||
420 | if (daemon->dhcp6 && | |
421 | setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) | |
422 | return 0; | |
423 | ||
424 | inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr); | |
425 | ||
426 | if (daemon->ra_contexts && | |
427 | setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) | |
428 | return 0; | |
429 | ||
430 | expand_buf(&daemon->outpacket, (max+1) * sizeof(int)); | |
431 | ((int *)daemon->outpacket.iov_base)[max++] = if_index; | |
432 | ||
433 | *((int *)vparam) = max; | |
434 | ||
435 | return 1; | |
436 | } | |
437 | ||
438 | void join_multicast(void) | |
439 | { | |
440 | int count = 0; | |
441 | ||
442 | if (!iface_enumerate(AF_INET6, &count, join_multicast_worker)) | |
443 | die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET); | |
444 | } | |
445 | #endif | |
446 | ||
40ef23b5 SK |
447 | |
448 | static const struct opttab_t { | |
449 | char *name; | |
450 | u16 val, size; | |
451 | } opttab[] = { | |
452 | { "netmask", 1, OT_ADDR_LIST }, | |
453 | { "time-offset", 2, 4 }, | |
454 | { "router", 3, OT_ADDR_LIST }, | |
455 | { "dns-server", 6, OT_ADDR_LIST }, | |
456 | { "log-server", 7, OT_ADDR_LIST }, | |
457 | { "lpr-server", 9, OT_ADDR_LIST }, | |
458 | { "hostname", 12, OT_INTERNAL | OT_NAME }, | |
459 | { "boot-file-size", 13, 2 | OT_DEC }, | |
460 | { "domain-name", 15, OT_NAME }, | |
461 | { "swap-server", 16, OT_ADDR_LIST }, | |
462 | { "root-path", 17, OT_NAME }, | |
463 | { "extension-path", 18, OT_NAME }, | |
464 | { "ip-forward-enable", 19, 1 }, | |
465 | { "non-local-source-routing", 20, 1 }, | |
466 | { "policy-filter", 21, OT_ADDR_LIST }, | |
467 | { "max-datagram-reassembly", 22, 2 | OT_DEC }, | |
468 | { "default-ttl", 23, 1 | OT_DEC }, | |
469 | { "mtu", 26, 2 | OT_DEC }, | |
470 | { "all-subnets-local", 27, 1 }, | |
471 | { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST }, | |
472 | { "router-discovery", 31, 1 }, | |
473 | { "router-solicitation", 32, OT_ADDR_LIST }, | |
474 | { "static-route", 33, OT_ADDR_LIST }, | |
475 | { "trailer-encapsulation", 34, 1 }, | |
476 | { "arp-timeout", 35, 4 | OT_DEC }, | |
477 | { "ethernet-encap", 36, 1 }, | |
478 | { "tcp-ttl", 37, 1 }, | |
479 | { "tcp-keepalive", 38, 4 | OT_DEC }, | |
480 | { "nis-domain", 40, OT_NAME }, | |
481 | { "nis-server", 41, OT_ADDR_LIST }, | |
482 | { "ntp-server", 42, OT_ADDR_LIST }, | |
483 | { "vendor-encap", 43, OT_INTERNAL }, | |
484 | { "netbios-ns", 44, OT_ADDR_LIST }, | |
485 | { "netbios-dd", 45, OT_ADDR_LIST }, | |
486 | { "netbios-nodetype", 46, 1 }, | |
487 | { "netbios-scope", 47, 0 }, | |
488 | { "x-windows-fs", 48, OT_ADDR_LIST }, | |
489 | { "x-windows-dm", 49, OT_ADDR_LIST }, | |
490 | { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST }, | |
491 | { "lease-time", 51, OT_INTERNAL | OT_DEC }, | |
492 | { "option-overload", 52, OT_INTERNAL }, | |
493 | { "message-type", 53, OT_INTERNAL | OT_DEC }, | |
494 | { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST }, | |
495 | { "parameter-request", 55, OT_INTERNAL }, | |
496 | { "message", 56, OT_INTERNAL }, | |
497 | { "max-message-size", 57, OT_INTERNAL }, | |
498 | { "T1", 58, OT_INTERNAL | OT_DEC}, | |
499 | { "T2", 59, OT_INTERNAL | OT_DEC}, | |
500 | { "vendor-class", 60, 0 }, | |
501 | { "client-id", 61, OT_INTERNAL }, | |
502 | { "nis+-domain", 64, OT_NAME }, | |
503 | { "nis+-server", 65, OT_ADDR_LIST }, | |
504 | { "tftp-server", 66, OT_NAME }, | |
505 | { "bootfile-name", 67, OT_NAME }, | |
506 | { "mobile-ip-home", 68, OT_ADDR_LIST }, | |
507 | { "smtp-server", 69, OT_ADDR_LIST }, | |
508 | { "pop3-server", 70, OT_ADDR_LIST }, | |
509 | { "nntp-server", 71, OT_ADDR_LIST }, | |
510 | { "irc-server", 74, OT_ADDR_LIST }, | |
511 | { "user-class", 77, 0 }, | |
512 | { "FQDN", 81, OT_INTERNAL }, | |
513 | { "agent-id", 82, OT_INTERNAL }, | |
514 | { "client-arch", 93, 2 | OT_DEC }, | |
515 | { "client-interface-id", 94, 0 }, | |
516 | { "client-machine-id", 97, 0 }, | |
517 | { "subnet-select", 118, OT_INTERNAL }, | |
518 | { "domain-search", 119, OT_RFC1035_NAME }, | |
519 | { "sip-server", 120, 0 }, | |
520 | { "classless-static-route", 121, 0 }, | |
521 | { "vendor-id-encap", 125, 0 }, | |
522 | { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */ | |
523 | { NULL, 0, 0 } | |
524 | }; | |
525 | ||
526 | #ifdef HAVE_DHCP6 | |
527 | static const struct opttab_t opttab6[] = { | |
528 | { "client-id", 1, OT_INTERNAL }, | |
529 | { "server-id", 2, OT_INTERNAL }, | |
530 | { "ia-na", 3, OT_INTERNAL }, | |
531 | { "ia-ta", 4, OT_INTERNAL }, | |
532 | { "iaaddr", 5, OT_INTERNAL }, | |
533 | { "oro", 6, OT_INTERNAL }, | |
534 | { "preference", 7, OT_INTERNAL | OT_DEC }, | |
535 | { "unicast", 12, OT_INTERNAL }, | |
536 | { "status", 13, OT_INTERNAL }, | |
537 | { "rapid-commit", 14, OT_INTERNAL }, | |
538 | { "user-class", 15, OT_INTERNAL | OT_CSTRING }, | |
539 | { "vendor-class", 16, OT_INTERNAL | OT_CSTRING }, | |
540 | { "vendor-opts", 17, OT_INTERNAL }, | |
541 | { "sip-server-domain", 21, OT_RFC1035_NAME }, | |
542 | { "sip-server", 22, OT_ADDR_LIST }, | |
543 | { "dns-server", 23, OT_ADDR_LIST }, | |
544 | { "domain-search", 24, OT_RFC1035_NAME }, | |
545 | { "nis-server", 27, OT_ADDR_LIST }, | |
546 | { "nis+-server", 28, OT_ADDR_LIST }, | |
547 | { "nis-domain", 29, OT_RFC1035_NAME }, | |
548 | { "nis+-domain", 30, OT_RFC1035_NAME }, | |
549 | { "sntp-server", 31, OT_ADDR_LIST }, | |
550 | { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME }, | |
551 | { "ntp-server", 56, OT_ADDR_LIST }, | |
552 | { "bootfile-url", 59, OT_NAME }, | |
553 | { "bootfile-param", 60, OT_CSTRING }, | |
554 | { NULL, 0, 0 } | |
555 | }; | |
556 | #endif | |
557 | ||
558 | ||
559 | ||
560 | void display_opts(void) | |
561 | { | |
562 | int i; | |
563 | ||
564 | printf(_("Known DHCP options:\n")); | |
565 | ||
566 | for (i = 0; opttab[i].name; i++) | |
567 | if (!(opttab[i].size & OT_INTERNAL)) | |
568 | printf("%3d %s\n", opttab[i].val, opttab[i].name); | |
569 | } | |
570 | ||
571 | #ifdef HAVE_DHCP6 | |
572 | void display_opts6(void) | |
573 | { | |
574 | int i; | |
575 | printf(_("Known DHCPv6 options:\n")); | |
576 | ||
577 | for (i = 0; opttab6[i].name; i++) | |
578 | if (!(opttab6[i].size & OT_INTERNAL)) | |
579 | printf("%3d %s\n", opttab6[i].val, opttab6[i].name); | |
580 | } | |
581 | #endif | |
582 | ||
583 | u16 lookup_dhcp_opt(int prot, char *name) | |
584 | { | |
585 | const struct opttab_t *t; | |
586 | int i; | |
587 | ||
588 | #ifdef HAVE_DHCP6 | |
589 | if (prot == AF_INET6) | |
590 | t = opttab6; | |
591 | else | |
592 | #endif | |
593 | t = opttab; | |
594 | ||
595 | for (i = 0; t[i].name; i++) | |
596 | if (!(t[i].size & OT_INTERNAL) && | |
597 | strcasecmp(t[i].name, name) == 0) | |
598 | return t[i].val; | |
599 | ||
600 | return 0; | |
601 | } | |
602 | ||
603 | u16 lookup_dhcp_len(int prot, u16 val) | |
604 | { | |
605 | const struct opttab_t *t; | |
606 | int i; | |
607 | ||
608 | #ifdef HAVE_DHCP6 | |
609 | if (prot == AF_INET6) | |
610 | t = opttab6; | |
611 | else | |
612 | #endif | |
613 | t = opttab; | |
614 | ||
615 | for (i = 0; t[i].name; i++) | |
616 | if (val == t[i].val) | |
617 | { | |
618 | if (t[i].size & OT_INTERNAL) | |
619 | return 0; | |
620 | ||
621 | return t[i].size & ~OT_DEC; | |
622 | } | |
623 | ||
624 | return 0; | |
625 | } | |
626 | ||
627 | char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len) | |
628 | { | |
629 | int o, i, j, nodecode = 0; | |
630 | const struct opttab_t *ot = opttab; | |
631 | ||
632 | #ifdef HAVE_DHCP6 | |
633 | if (prot == AF_INET6) | |
634 | ot = opttab6; | |
635 | #endif | |
636 | ||
637 | for (o = 0; ot[o].name; o++) | |
638 | if (ot[o].val == opt) | |
639 | { | |
640 | if (buf) | |
641 | { | |
642 | memset(buf, 0, buf_len); | |
643 | ||
644 | if (ot[o].size & OT_ADDR_LIST) | |
645 | { | |
646 | struct all_addr addr; | |
647 | int addr_len = INADDRSZ; | |
648 | ||
649 | #ifdef HAVE_DHCP6 | |
650 | if (prot == AF_INET6) | |
651 | addr_len = IN6ADDRSZ; | |
652 | #endif | |
653 | for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len) | |
654 | { | |
655 | if (i != 0) | |
656 | strncat(buf, ", ", buf_len - strlen(buf)); | |
657 | /* align */ | |
658 | memcpy(&addr, &val[i], addr_len); | |
659 | inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN); | |
660 | strncat(buf, daemon->addrbuff, buf_len - strlen(buf)); | |
661 | } | |
662 | } | |
663 | else if (ot[o].size & OT_NAME) | |
664 | for (i = 0, j = 0; i < opt_len && j < buf_len ; i++) | |
665 | { | |
666 | char c = val[i]; | |
667 | if (isprint((int)c)) | |
668 | buf[j++] = c; | |
669 | } | |
670 | #ifdef HAVE_DHCP6 | |
671 | /* We don't handle compressed rfc1035 names, so no good in IPv4 land */ | |
672 | else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6) | |
673 | { | |
674 | i = 0, j = 0; | |
675 | while (i < opt_len && val[i] != 0) | |
676 | { | |
677 | int k, l = i + val[i] + 1; | |
678 | for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++) | |
679 | { | |
680 | char c = val[k]; | |
681 | if (isprint((int)c)) | |
682 | buf[j++] = c; | |
683 | } | |
684 | i = l; | |
685 | if (val[i] != 0 && j < buf_len) | |
686 | buf[j++] = '.'; | |
687 | } | |
688 | } | |
689 | else if ((ot[o].size & OT_CSTRING)) | |
690 | { | |
691 | int k, len; | |
692 | unsigned char *p; | |
693 | ||
694 | i = 0, j = 0; | |
695 | while (1) | |
696 | { | |
697 | p = &val[i]; | |
698 | GETSHORT(len, p); | |
699 | for (k = 0; k < len && j < buf_len; k++) | |
700 | { | |
701 | char c = *p++; | |
702 | if (isprint((int)c)) | |
703 | buf[j++] = c; | |
704 | } | |
705 | i += len +2; | |
706 | if (i >= opt_len) | |
707 | break; | |
708 | ||
709 | if (j < buf_len) | |
710 | buf[j++] = ','; | |
711 | } | |
712 | } | |
713 | #endif | |
714 | else if ((ot[o].size & OT_DEC) && opt_len != 0) | |
715 | { | |
716 | unsigned int dec = 0; | |
717 | ||
718 | for (i = 0; i < opt_len; i++) | |
719 | dec = (dec << 8) | val[i]; | |
720 | ||
721 | sprintf(buf, "%u", dec); | |
722 | } | |
723 | else | |
724 | nodecode = 1; | |
725 | } | |
726 | break; | |
727 | } | |
728 | ||
729 | if (opt_len != 0 && buf && (!ot[o].name || nodecode)) | |
730 | { | |
731 | int trunc = 0; | |
732 | if (opt_len > 14) | |
733 | { | |
734 | trunc = 1; | |
735 | opt_len = 14; | |
736 | } | |
737 | print_mac(buf, val, opt_len); | |
738 | if (trunc) | |
739 | strncat(buf, "...", buf_len - strlen(buf)); | |
740 | ||
741 | ||
742 | } | |
743 | ||
744 | return ot[o].name ? ot[o].name : ""; | |
745 | ||
746 | } | |
747 | ||
4cb1b320 | 748 | #endif |