blob: 2e404479bc81c62a912086242de55b9c079cf0f7 [file] [log] [blame]
Simon Kelley4cb1b322012-02-06 14:30:41 +00001/* 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
21void 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
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 */
56 if ((size_t)sz == daemon->dhcp_packet.iov_len)
57 {
58 if (!expand_buf(&daemon->dhcp_packet, sz + 100))
59 return -1;
60 }
61 else
62 {
63 expand_buf(&daemon->dhcp_packet, sz);
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
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;
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,
105 otherwise valid options are inhibited if we found a higher priotity one above */
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 */
146int 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. */
173char *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
187void 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
Simon Kelley3634c542012-02-08 14:22:37 +0000212int 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}
Simon Kelleyceae00d2012-02-09 21:28:14 +0000241
242void 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
276void 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
335 if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 129, 0))
336 {
337 memcpy(config->hwaddr, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
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}
Simon Kelley4cb1b322012-02-06 14:30:41 +0000360
361#endif