]>
Commit | Line | Data |
---|---|---|
cdeda28f | 1 | /* dnsmasq is Copyright (c) 2000-2006 Simon Kelley |
9e4abcb5 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. | |
6 | ||
7 | This program is distributed in the hope that it will be useful, | |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | GNU General Public License for more details. | |
11 | */ | |
12 | ||
9e4abcb5 SK |
13 | #include "dnsmasq.h" |
14 | ||
5e9e0efb SK |
15 | struct iface_param { |
16 | struct in_addr relay, primary; | |
17 | struct dhcp_context *current; | |
18 | int ind; | |
19 | }; | |
20 | ||
21 | static int complete_context(struct daemon *daemon, struct in_addr local, int if_index, | |
22 | struct in_addr netmask, struct in_addr broadcast, void *vparam); | |
23 | ||
3be34541 | 24 | void dhcp_init(struct daemon *daemon) |
44a2a316 SK |
25 | { |
26 | int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
27 | struct sockaddr_in saddr; | |
5e9e0efb | 28 | int flags, oneopt = 1; |
3be34541 | 29 | struct dhcp_config *configs, *cp; |
dfa666f2 | 30 | |
44a2a316 | 31 | if (fd == -1) |
b8187c80 | 32 | die (_("cannot create DHCP socket : %s"), NULL); |
44a2a316 | 33 | |
fd9fa481 SK |
34 | if ((flags = fcntl(fd, F_GETFL, 0)) == -1 || |
35 | fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 || | |
5e9e0efb | 36 | #if defined(HAVE_LINUX_NETWORK) |
3be34541 | 37 | setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 || |
44a2a316 | 38 | #elif defined(IP_RECVIF) |
3be34541 | 39 | setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 || |
44a2a316 | 40 | #endif |
3be34541 | 41 | setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1) |
b8187c80 | 42 | die(_("failed to set options on DHCP socket: %s"), NULL); |
44a2a316 | 43 | |
f6b7dc47 SK |
44 | /* When bind-interfaces is set, there might be more than one dnmsasq |
45 | instance binding port 67. That's Ok if they serve different networks. | |
46 | Need to set REUSEADDR to make this posible. */ | |
47 | if ((daemon->options & OPT_NOWILD) && | |
48 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)) == -1) | |
b8187c80 | 49 | die(_("failed to set SO_REUSEADDR on DHCP socket: %s"), NULL); |
f6b7dc47 | 50 | |
44a2a316 SK |
51 | saddr.sin_family = AF_INET; |
52 | saddr.sin_port = htons(DHCP_SERVER_PORT); | |
53 | saddr.sin_addr.s_addr = INADDR_ANY; | |
3be34541 SK |
54 | #ifdef HAVE_SOCKADDR_SA_LEN |
55 | saddr.sin_len = sizeof(struct sockaddr_in); | |
56 | #endif | |
57 | ||
44a2a316 | 58 | if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in))) |
b8187c80 | 59 | die(_("failed to bind DHCP server socket: %s"), NULL); |
44a2a316 | 60 | |
3be34541 SK |
61 | daemon->dhcpfd = fd; |
62 | ||
5e9e0efb SK |
63 | #ifndef HAVE_LINUX_NETWORK |
64 | /* When we're not using capabilities, we need to do this here before | |
65 | we drop root. Also, set buffer size small, to avoid wasting | |
66 | kernel buffers */ | |
67 | ||
68 | if (daemon->options & OPT_NO_PING) | |
69 | daemon->dhcp_icmp_fd = -1; | |
70 | else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 || | |
71 | setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 ) | |
b8187c80 | 72 | die(_("cannot create ICMP raw socket: %s."), NULL); |
44a2a316 | 73 | |
5e9e0efb SK |
74 | /* Make BPF raw send socket */ |
75 | init_bpf(daemon); | |
76 | #endif | |
3be34541 | 77 | |
dfa666f2 SK |
78 | /* If the same IP appears in more than one host config, then DISCOVER |
79 | for one of the hosts will get the address, but REQUEST will be NAKed, | |
80 | since the address is reserved by the other one -> protocol loop. */ | |
3be34541 | 81 | for (configs = daemon->dhcp_conf; configs; configs = configs->next) |
dfa666f2 SK |
82 | for (cp = configs->next; cp; cp = cp->next) |
83 | if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr) | |
b8187c80 | 84 | die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr)); |
3be34541 | 85 | |
5e9e0efb SK |
86 | daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet); |
87 | daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len); | |
88 | /* These two each hold a DHCP option max size 255 | |
3be34541 | 89 | and get a terminating zero added */ |
0a852541 SK |
90 | daemon->dhcp_buff = safe_malloc(256); |
91 | daemon->dhcp_buff2 = safe_malloc(256); | |
3d8df260 | 92 | daemon->ping_results = NULL; |
44a2a316 | 93 | } |
5e9e0efb | 94 | |
3be34541 | 95 | void dhcp_packet(struct daemon *daemon, time_t now) |
9e4abcb5 | 96 | { |
5e9e0efb | 97 | struct dhcp_packet *mess; |
44a2a316 SK |
98 | struct dhcp_context *context; |
99 | struct iname *tmp; | |
100 | struct ifreq ifr; | |
101 | struct msghdr msg; | |
5e9e0efb | 102 | struct sockaddr_in dest; |
44a2a316 | 103 | struct cmsghdr *cmptr; |
5e9e0efb SK |
104 | struct iovec iov; |
105 | ssize_t sz; | |
cdeda28f | 106 | int iface_index = 0, unicast_dest = 0; |
0a852541 | 107 | struct in_addr iface_addr; |
5e9e0efb | 108 | struct iface_param parm; |
9e4abcb5 | 109 | |
44a2a316 SK |
110 | union { |
111 | struct cmsghdr align; /* this ensures alignment */ | |
5e9e0efb | 112 | #ifdef HAVE_LINUX_NETWORK |
44a2a316 SK |
113 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
114 | #else | |
115 | char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; | |
9e4abcb5 | 116 | #endif |
44a2a316 SK |
117 | } control_u; |
118 | ||
44a2a316 SK |
119 | msg.msg_control = control_u.control; |
120 | msg.msg_controllen = sizeof(control_u); | |
44a2a316 SK |
121 | msg.msg_name = NULL; |
122 | msg.msg_namelen = 0; | |
5e9e0efb | 123 | msg.msg_iov = &daemon->dhcp_packet; |
44a2a316 SK |
124 | msg.msg_iovlen = 1; |
125 | ||
5e9e0efb SK |
126 | do |
127 | { | |
128 | msg.msg_flags = 0; | |
129 | while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK)) == -1 && errno == EINTR); | |
130 | } | |
131 | while (sz != -1 && (msg.msg_flags & MSG_TRUNC) && | |
132 | expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100)); | |
44a2a316 | 133 | |
5e9e0efb SK |
134 | /* expand_buf may have moved buffer */ |
135 | mess = daemon->dhcp_packet.iov_base; | |
136 | msg.msg_controllen = sizeof(control_u); | |
137 | msg.msg_flags = 0; | |
138 | msg.msg_name = &dest; | |
139 | msg.msg_namelen = sizeof(dest); | |
140 | ||
141 | while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) && errno == EINTR); | |
142 | ||
143 | if ((msg.msg_flags & MSG_TRUNC) || | |
144 | sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))) | |
44a2a316 SK |
145 | return; |
146 | ||
5e9e0efb | 147 | #if defined (HAVE_LINUX_NETWORK) |
44a2a316 SK |
148 | if (msg.msg_controllen < sizeof(struct cmsghdr)) |
149 | return; | |
150 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
151 | if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) | |
b8187c80 SK |
152 | { |
153 | iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex; | |
154 | if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST) | |
155 | unicast_dest = 1; | |
156 | } | |
157 | ||
8a911ccc | 158 | if (!(ifr.ifr_ifindex = iface_index) || |
3be34541 | 159 | ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1) |
44a2a316 | 160 | return; |
b8187c80 | 161 | |
44a2a316 SK |
162 | #elif defined(IP_RECVIF) |
163 | if (msg.msg_controllen < sizeof(struct cmsghdr)) | |
164 | return; | |
165 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
166 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) | |
167 | iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index; | |
b8187c80 | 168 | |
44a2a316 SK |
169 | if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name)) |
170 | return; | |
171 | ||
172 | #else | |
3be34541 SK |
173 | { |
174 | struct iname *name; | |
fd9fa481 | 175 | for (name = daemon->if_names; name->isloop; name = name->next); |
3be34541 | 176 | strcpy(ifr.ifr_name, name->name); |
5e9e0efb | 177 | iface_index = if_nametoindex(name->name); |
3be34541 | 178 | } |
9e4abcb5 SK |
179 | #endif |
180 | ||
44a2a316 | 181 | ifr.ifr_addr.sa_family = AF_INET; |
3be34541 | 182 | if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 ) |
44a2a316 SK |
183 | return; |
184 | iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; | |
185 | ||
3d8df260 SK |
186 | for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) |
187 | if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) | |
188 | return; | |
44a2a316 | 189 | |
5e9e0efb SK |
190 | if (!iface_check(daemon, AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name)) |
191 | return; | |
44a2a316 | 192 | |
91dccd09 | 193 | /* unlinked contexts are marked by context->current == context */ |
3be34541 | 194 | for (context = daemon->dhcp; context; context = context->next) |
91dccd09 | 195 | context->current = context; |
0a852541 | 196 | |
5e9e0efb SK |
197 | parm.relay = mess->giaddr; |
198 | parm.primary = iface_addr; | |
199 | parm.current = NULL; | |
200 | parm.ind = iface_index; | |
0a852541 | 201 | |
5e9e0efb SK |
202 | if (!iface_enumerate(daemon, &parm, complete_context, NULL)) |
203 | return; | |
44a2a316 | 204 | lease_prune(NULL, now); /* lose any expired leases */ |
5e9e0efb SK |
205 | iov.iov_len = dhcp_reply(daemon, parm.current, ifr.ifr_name, (size_t)sz, now, unicast_dest); |
206 | lease_update_file(daemon); | |
fd9fa481 | 207 | lease_update_dns(daemon); |
44a2a316 | 208 | |
5e9e0efb | 209 | if (iov.iov_len == 0) |
44a2a316 SK |
210 | return; |
211 | ||
5e9e0efb SK |
212 | msg.msg_name = &dest; |
213 | msg.msg_namelen = sizeof(dest); | |
214 | msg.msg_control = NULL; | |
215 | msg.msg_controllen = 0; | |
216 | msg.msg_iov = &iov; | |
217 | iov.iov_base = daemon->dhcp_packet.iov_base; | |
218 | ||
219 | /* packet buffer may have moved */ | |
220 | mess = daemon->dhcp_packet.iov_base; | |
221 | ||
3be34541 | 222 | #ifdef HAVE_SOCKADDR_SA_LEN |
5e9e0efb | 223 | dest.sin_len = sizeof(struct sockaddr_in); |
3be34541 SK |
224 | #endif |
225 | ||
5e9e0efb SK |
226 | if (mess->giaddr.s_addr) |
227 | { | |
228 | /* Send to BOOTP relay */ | |
229 | if (!dest.sin_port) | |
230 | dest.sin_port = htons(DHCP_SERVER_PORT); | |
231 | dest.sin_addr = mess->giaddr; | |
232 | } | |
233 | else if (mess->ciaddr.s_addr) | |
234 | { | |
235 | dest.sin_addr = mess->ciaddr; | |
236 | if (!dest.sin_port) | |
237 | dest.sin_port = htons(DHCP_CLIENT_PORT); | |
238 | } | |
239 | #ifdef HAVE_LINUX_NETWORK | |
240 | else if (ntohs(mess->flags) & 0x8000) | |
241 | { | |
242 | /* broadcast to 255.255.255.255 */ | |
243 | struct in_pktinfo *pkt; | |
244 | msg.msg_controllen = sizeof(control_u); | |
245 | cmptr = CMSG_FIRSTHDR(&msg); | |
246 | dest.sin_addr.s_addr = INADDR_BROADCAST; | |
247 | dest.sin_port = htons(DHCP_CLIENT_PORT); | |
248 | pkt = (struct in_pktinfo *)CMSG_DATA(cmptr); | |
249 | pkt->ipi_ifindex = iface_index; | |
250 | pkt->ipi_spec_dst.s_addr = 0; | |
251 | msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); | |
252 | cmptr->cmsg_level = SOL_IP; | |
253 | cmptr->cmsg_type = IP_PKTINFO; | |
44a2a316 SK |
254 | } |
255 | else | |
256 | { | |
5e9e0efb SK |
257 | /* unicast to unconfigured client */ |
258 | dest.sin_addr = mess->yiaddr; | |
259 | dest.sin_port = htons(DHCP_CLIENT_PORT); | |
260 | if (mess->hlen != 0 && mess->htype != 0) | |
261 | arp_inject(daemon->netlinkfd, mess->yiaddr, iface_index, | |
262 | mess->chaddr, mess->hlen); | |
263 | } | |
44a2a316 | 264 | #else |
5e9e0efb SK |
265 | else |
266 | { | |
267 | send_via_bpf(daemon, mess, iov.iov_len, iface_addr, &ifr); | |
268 | return; | |
9e4abcb5 | 269 | } |
5e9e0efb SK |
270 | #endif |
271 | ||
272 | while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send()); | |
9e4abcb5 | 273 | } |
5e9e0efb | 274 | |
0a852541 | 275 | /* This is a complex routine: it gets called with each (address,netmask,broadcast) triple |
5e9e0efb SK |
276 | of each interface (and any relay address) and does the following things: |
277 | ||
278 | 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived. | |
279 | 2) Fills in any netmask and broadcast addresses which have not been explicitly configured. | |
280 | 3) Fills in local (this host) and router (this host or relay) addresses. | |
281 | 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current. | |
282 | ||
0a852541 | 283 | Note that the current chain may be superceded later for configured hosts or those coming via gateways. */ |
5e9e0efb SK |
284 | |
285 | static int complete_context(struct daemon *daemon, struct in_addr local, int if_index, | |
286 | struct in_addr netmask, struct in_addr broadcast, void *vparam) | |
0a852541 SK |
287 | { |
288 | struct dhcp_context *context; | |
5e9e0efb SK |
289 | struct iface_param *param = vparam; |
290 | ||
291 | if (if_index != param->ind) | |
292 | return 1; /* no for us. */ | |
0a852541 SK |
293 | |
294 | for (context = daemon->dhcp; context; context = context->next) | |
295 | { | |
296 | if (!(context->flags & CONTEXT_NETMASK) && | |
297 | (is_same_net(local, context->start, netmask) || | |
298 | is_same_net(local, context->end, netmask))) | |
299 | { | |
300 | if (context->netmask.s_addr != netmask.s_addr && | |
301 | !(is_same_net(local, context->start, netmask) && | |
302 | is_same_net(local, context->end, netmask))) | |
303 | { | |
304 | strcpy(daemon->dhcp_buff, inet_ntoa(context->start)); | |
305 | strcpy(daemon->dhcp_buff2, inet_ntoa(context->end)); | |
b8187c80 | 306 | syslog(LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"), |
0a852541 SK |
307 | daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask)); |
308 | } | |
309 | context->netmask = netmask; | |
310 | } | |
311 | ||
312 | if (context->netmask.s_addr) | |
313 | { | |
314 | if (is_same_net(local, context->start, context->netmask) && | |
315 | is_same_net(local, context->end, context->netmask)) | |
316 | { | |
91dccd09 SK |
317 | /* link it onto the current chain if we've not seen it before */ |
318 | if (context->current == context) | |
0a852541 SK |
319 | { |
320 | context->router = local; | |
321 | context->local = local; | |
5e9e0efb SK |
322 | context->current = param->current; |
323 | param->current = context; | |
0a852541 SK |
324 | } |
325 | ||
326 | if (!(context->flags & CONTEXT_BRDCAST)) | |
327 | { | |
328 | if (is_same_net(broadcast, context->start, context->netmask)) | |
329 | context->broadcast = broadcast; | |
330 | else | |
331 | context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; | |
332 | } | |
333 | } | |
5e9e0efb | 334 | else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask)) |
0a852541 | 335 | { |
5e9e0efb SK |
336 | context->router = param->relay; |
337 | context->local = param->primary; | |
0a852541 SK |
338 | /* fill in missing broadcast addresses for relayed ranges */ |
339 | if (!(context->flags & CONTEXT_BRDCAST)) | |
340 | context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; | |
341 | } | |
342 | ||
343 | } | |
344 | } | |
345 | ||
5e9e0efb | 346 | return 1; |
0a852541 SK |
347 | } |
348 | ||
59353a6b | 349 | struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr) |
9e4abcb5 | 350 | { |
36717eee | 351 | /* Check is an address is OK for this network, check all |
5e9e0efb SK |
352 | possible ranges. Make sure that the address isn't in use |
353 | by the server itself. */ | |
9e4abcb5 | 354 | |
36717eee | 355 | unsigned int start, end, addr = ntohl(taddr.s_addr); |
5e9e0efb SK |
356 | struct dhcp_context *tmp; |
357 | ||
358 | for (tmp = context; tmp; tmp = tmp->current) | |
359 | if (taddr.s_addr == context->local.s_addr) | |
360 | return NULL; | |
9e4abcb5 | 361 | |
5e9e0efb | 362 | for (tmp = context; tmp; tmp = tmp->current) |
36717eee | 363 | { |
5e9e0efb SK |
364 | start = ntohl(tmp->start.s_addr); |
365 | end = ntohl(tmp->end.s_addr); | |
9e4abcb5 | 366 | |
5e9e0efb | 367 | if (!(tmp->flags & CONTEXT_STATIC) && |
36717eee SK |
368 | addr >= start && |
369 | addr <= end) | |
5e9e0efb | 370 | return tmp; |
36717eee | 371 | } |
9e4abcb5 | 372 | |
59353a6b | 373 | return NULL; |
9e4abcb5 | 374 | } |
59353a6b SK |
375 | |
376 | struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr) | |
377 | { | |
e17fb629 | 378 | /* We start of with a set of possible contexts, all on the current physical interface. |
59353a6b SK |
379 | These are chained on ->current. |
380 | Here we have an address, and return the actual context correponding to that | |
381 | address. Note that none may fit, if the address came a dhcp-host and is outside | |
e17fb629 SK |
382 | any dhcp-range. In that case we return a static range if possible, or failing that, |
383 | any context on the correct subnet. (If there's more than one, this is a dodgy | |
384 | configuration: maybe there should be a warning.) */ | |
59353a6b | 385 | |
e17fb629 | 386 | struct dhcp_context *tmp; |
59353a6b | 387 | |
e17fb629 | 388 | if ((tmp = address_available(context, taddr))) |
59353a6b SK |
389 | return tmp; |
390 | ||
391 | for (tmp = context; tmp; tmp = tmp->current) | |
e17fb629 SK |
392 | if (is_same_net(taddr, tmp->start, tmp->netmask) && |
393 | (tmp->flags & CONTEXT_STATIC)) | |
394 | return tmp; | |
395 | ||
396 | for (tmp = context; tmp; tmp = tmp->current) | |
397 | if (is_same_net(taddr, tmp->start, tmp->netmask)) | |
59353a6b SK |
398 | return tmp; |
399 | ||
e17fb629 | 400 | return NULL; |
59353a6b SK |
401 | } |
402 | ||
dfa666f2 SK |
403 | struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr) |
404 | { | |
405 | struct dhcp_config *config; | |
406 | ||
407 | for (config = configs; config; config = config->next) | |
408 | if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) | |
409 | return config; | |
410 | ||
411 | return NULL; | |
412 | } | |
9e4abcb5 | 413 | |
cdeda28f SK |
414 | /* Is every member of check matched by a member of pool? |
415 | If negonly, match unless there's a negative tag which matches. */ | |
416 | int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly) | |
f6b7dc47 SK |
417 | { |
418 | struct dhcp_netid *tmp1; | |
419 | ||
cdeda28f | 420 | if (!check && !negonly) |
f6b7dc47 SK |
421 | return 0; |
422 | ||
423 | for (; check; check = check->next) | |
424 | { | |
425 | if (check->net[0] != '#') | |
426 | { | |
427 | for (tmp1 = pool; tmp1; tmp1 = tmp1->next) | |
428 | if (strcmp(check->net, tmp1->net) == 0) | |
429 | break; | |
cdeda28f | 430 | if (!tmp1 || negonly) |
f6b7dc47 SK |
431 | return 0; |
432 | } | |
433 | else | |
434 | for (tmp1 = pool; tmp1; tmp1 = tmp1->next) | |
435 | if (strcmp((check->net)+1, tmp1->net) == 0) | |
436 | return 0; | |
437 | } | |
438 | return 1; | |
439 | } | |
440 | ||
3be34541 | 441 | int address_allocate(struct dhcp_context *context, struct daemon *daemon, |
cdeda28f | 442 | struct in_addr *addrp, unsigned char *hwaddr, int hw_len, |
3d8df260 | 443 | struct dhcp_netid *netids, time_t now) |
9e4abcb5 | 444 | { |
feba5c1d | 445 | /* Find a free address: exclude anything in use and anything allocated to |
f6b7dc47 | 446 | a particular hwaddr/clientid/hostname in our configuration. |
cdeda28f | 447 | Try to return from contexts which match netids first. */ |
9e4abcb5 | 448 | |
5e9e0efb SK |
449 | struct in_addr start, addr; |
450 | struct dhcp_context *c, *d; | |
cdeda28f SK |
451 | int i, pass; |
452 | unsigned int j; | |
453 | ||
5e9e0efb SK |
454 | /* hash hwaddr */ |
455 | for (j = 0, i = 0; i < hw_len; i++) | |
456 | j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16); | |
457 | ||
cdeda28f SK |
458 | for (pass = 0; pass <= 1; pass++) |
459 | for (c = context; c; c = c->current) | |
460 | if (c->flags & CONTEXT_STATIC) | |
461 | continue; | |
462 | else if (!match_netid(c->filter, netids, pass)) | |
463 | continue; | |
464 | else | |
465 | { | |
466 | /* pick a seed based on hwaddr then iterate until we find a free address. */ | |
cdeda28f SK |
467 | start.s_addr = addr.s_addr = |
468 | htonl(ntohl(c->start.s_addr) + | |
5e9e0efb | 469 | ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr)))); |
cdeda28f SK |
470 | |
471 | do { | |
5e9e0efb SK |
472 | /* eliminate addresses in use by the server. */ |
473 | for (d = context; d; d = d->current) | |
474 | if (addr.s_addr == d->local.s_addr) | |
475 | break; | |
476 | ||
477 | if (!d && | |
478 | !lease_find_by_addr(addr) && | |
cdeda28f SK |
479 | !config_find_by_address(daemon->dhcp_conf, addr)) |
480 | { | |
481 | struct ping_result *r, *victim = NULL; | |
5e9e0efb SK |
482 | int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/ |
483 | ((float)PING_WAIT))); | |
484 | ||
485 | *addrp = addr; | |
486 | ||
487 | if (daemon->options & OPT_NO_PING) | |
488 | return 1; | |
cdeda28f SK |
489 | |
490 | /* check if we failed to ping addr sometime in the last | |
5e9e0efb | 491 | PING_CACHE_TIME seconds. If so, assume the same situation still exists. |
cdeda28f | 492 | This avoids problems when a stupid client bangs |
5e9e0efb SK |
493 | on us repeatedly. As a final check, if we did more |
494 | than 60% of the possible ping checks in the last | |
495 | PING_CACHE_TIME, we are in high-load mode, so don't do any more. */ | |
cdeda28f | 496 | for (count = 0, r = daemon->ping_results; r; r = r->next) |
5e9e0efb | 497 | if (difftime(now, r->time) > (float)PING_CACHE_TIME) |
cdeda28f | 498 | victim = r; /* old record */ |
5e9e0efb SK |
499 | else if (++count == max || r->addr.s_addr == addr.s_addr) |
500 | return 1; | |
501 | ||
cdeda28f SK |
502 | if (icmp_ping(daemon, addr)) |
503 | /* address in use: perturb address selection so that we are | |
504 | less likely to try this address again. */ | |
505 | c->addr_epoch++; | |
506 | else | |
3d8df260 | 507 | { |
cdeda28f SK |
508 | /* at this point victim may hold an expired record */ |
509 | if (!victim) | |
510 | { | |
511 | if ((victim = malloc(sizeof(struct ping_result)))) | |
512 | { | |
513 | victim->next = daemon->ping_results; | |
514 | daemon->ping_results = victim; | |
515 | } | |
516 | } | |
517 | ||
518 | /* record that this address is OK for 30s | |
519 | without more ping checks */ | |
520 | if (victim) | |
521 | { | |
522 | victim->addr = addr; | |
523 | victim->time = now; | |
524 | } | |
3d8df260 SK |
525 | return 1; |
526 | } | |
cdeda28f SK |
527 | } |
528 | ||
529 | addr.s_addr = htonl(ntohl(addr.s_addr) + 1); | |
530 | ||
531 | if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1)) | |
532 | addr = c->start; | |
533 | ||
534 | } while (addr.s_addr != start.s_addr); | |
535 | } | |
9e4abcb5 SK |
536 | return 0; |
537 | } | |
538 | ||
539 | static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config) | |
540 | { | |
b8187c80 | 541 | if (!context) /* called via find_config() from lease_update_from_configs() */ |
0a852541 | 542 | return 1; |
33820b7e | 543 | if (!(config->flags & CONFIG_ADDR)) |
9e4abcb5 | 544 | return 1; |
b8187c80 SK |
545 | for (; context; context = context->current) |
546 | if (is_same_net(config->addr, context->start, context->netmask)) | |
547 | return 1; | |
9e4abcb5 SK |
548 | |
549 | return 0; | |
550 | } | |
551 | ||
0a852541 | 552 | |
9e4abcb5 SK |
553 | struct dhcp_config *find_config(struct dhcp_config *configs, |
554 | struct dhcp_context *context, | |
555 | unsigned char *clid, int clid_len, | |
cdeda28f SK |
556 | unsigned char *hwaddr, int hw_len, |
557 | int hw_type, char *hostname) | |
9e4abcb5 SK |
558 | { |
559 | struct dhcp_config *config; | |
560 | ||
0a852541 | 561 | if (clid) |
9e4abcb5 | 562 | for (config = configs; config; config = config->next) |
33820b7e SK |
563 | if (config->flags & CONFIG_CLID) |
564 | { | |
565 | if (config->clid_len == clid_len && | |
566 | memcmp(config->clid, clid, clid_len) == 0 && | |
567 | is_addr_in_context(context, config)) | |
568 | return config; | |
569 | ||
570 | /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and | |
571 | cope with that here */ | |
572 | if (*clid == 0 && config->clid_len == clid_len-1 && | |
573 | memcmp(config->clid, clid+1, clid_len-1) == 0 && | |
574 | is_addr_in_context(context, config)) | |
575 | return config; | |
576 | } | |
577 | ||
0a852541 | 578 | |
cdeda28f SK |
579 | for (config = configs; config; config = config->next) |
580 | if ((config->flags & CONFIG_HWADDR) && | |
581 | config->wildcard_mask == 0 && | |
582 | config->hwaddr_len == hw_len && | |
583 | (config->hwaddr_type == hw_type || config->hwaddr_type == 0) && | |
584 | memcmp(config->hwaddr, hwaddr, hw_len) == 0 && | |
585 | is_addr_in_context(context, config)) | |
586 | return config; | |
3d8df260 | 587 | |
0a852541 | 588 | if (hostname && context) |
9e4abcb5 | 589 | for (config = configs; config; config = config->next) |
33820b7e SK |
590 | if ((config->flags & CONFIG_NAME) && |
591 | hostname_isequal(config->hostname, hostname) && | |
9e4abcb5 SK |
592 | is_addr_in_context(context, config)) |
593 | return config; | |
594 | ||
cdeda28f SK |
595 | for (config = configs; config; config = config->next) |
596 | if ((config->flags & CONFIG_HWADDR) && | |
597 | config->wildcard_mask != 0 && | |
598 | config->hwaddr_len == hw_len && | |
599 | (config->hwaddr_type == hw_type || config->hwaddr_type == 0) && | |
5e9e0efb SK |
600 | is_addr_in_context(context, config) && |
601 | memcmp_masked(config->hwaddr, hwaddr, hw_len, config->wildcard_mask)) | |
602 | return config; | |
603 | ||
9e4abcb5 SK |
604 | return NULL; |
605 | } | |
606 | ||
3be34541 | 607 | void dhcp_read_ethers(struct daemon *daemon) |
44a2a316 SK |
608 | { |
609 | FILE *f = fopen(ETHERSFILE, "r"); | |
0a852541 | 610 | unsigned int flags; |
3be34541 | 611 | char *buff = daemon->namebuff; |
33820b7e | 612 | char *ip, *cp; |
44a2a316 | 613 | struct in_addr addr; |
33820b7e | 614 | unsigned char hwaddr[ETHER_ADDR_LEN]; |
3be34541 | 615 | struct dhcp_config *config, *configs = daemon->dhcp_conf; |
b8187c80 | 616 | int count = 0, lineno = 0; |
3d8df260 SK |
617 | |
618 | addr.s_addr = 0; /* eliminate warning */ | |
44a2a316 SK |
619 | |
620 | if (!f) | |
33820b7e | 621 | { |
b8187c80 | 622 | syslog(LOG_ERR, _("failed to read %s:%m"), ETHERSFILE); |
3be34541 | 623 | return; |
33820b7e SK |
624 | } |
625 | ||
44a2a316 SK |
626 | while (fgets(buff, MAXDNAME, f)) |
627 | { | |
b8187c80 SK |
628 | lineno++; |
629 | ||
33820b7e | 630 | while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1])) |
44a2a316 SK |
631 | buff[strlen(buff)-1] = 0; |
632 | ||
633 | if ((*buff == '#') || (*buff == '+')) | |
634 | continue; | |
635 | ||
33820b7e SK |
636 | for (ip = buff; *ip && !isspace(*ip); ip++); |
637 | for(; *ip && isspace(*ip); ip++) | |
44a2a316 | 638 | *ip = 0; |
cdeda28f | 639 | if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN) |
b8187c80 SK |
640 | { |
641 | syslog(LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno); | |
642 | continue; | |
643 | } | |
44a2a316 SK |
644 | |
645 | /* check for name or dotted-quad */ | |
646 | for (cp = ip; *cp; cp++) | |
647 | if (!(*cp == '.' || (*cp >='0' && *cp <= '9'))) | |
648 | break; | |
649 | ||
650 | if (!*cp) | |
651 | { | |
44a2a316 | 652 | if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1) |
b8187c80 SK |
653 | { |
654 | syslog(LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno); | |
655 | continue; | |
656 | } | |
657 | ||
33820b7e | 658 | flags = CONFIG_ADDR; |
1cff166d SK |
659 | |
660 | for (config = configs; config; config = config->next) | |
33820b7e | 661 | if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) |
1cff166d | 662 | break; |
44a2a316 SK |
663 | } |
664 | else | |
665 | { | |
1cff166d | 666 | if (!canonicalise(ip)) |
b8187c80 SK |
667 | { |
668 | syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno); | |
669 | continue; | |
670 | } | |
671 | ||
33820b7e | 672 | flags = CONFIG_NAME; |
1cff166d SK |
673 | |
674 | for (config = configs; config; config = config->next) | |
33820b7e | 675 | if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip)) |
1cff166d | 676 | break; |
44a2a316 SK |
677 | } |
678 | ||
1cff166d SK |
679 | if (!config) |
680 | { | |
33820b7e SK |
681 | for (config = configs; config; config = config->next) |
682 | if ((config->flags & CONFIG_HWADDR) && | |
91dccd09 | 683 | config->wildcard_mask == 0 && |
cdeda28f SK |
684 | config->hwaddr_len == ETHER_ADDR_LEN && |
685 | (config->hwaddr_type == ARPHRD_ETHER || config->hwaddr_type == 0) && | |
33820b7e SK |
686 | memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0) |
687 | break; | |
688 | ||
689 | if (!config) | |
690 | { | |
691 | if (!(config = malloc(sizeof(struct dhcp_config)))) | |
692 | continue; | |
693 | config->flags = 0; | |
91dccd09 | 694 | config->wildcard_mask = 0; |
33820b7e SK |
695 | config->next = configs; |
696 | configs = config; | |
697 | } | |
698 | ||
699 | config->flags |= flags; | |
700 | ||
701 | if (flags & CONFIG_NAME) | |
702 | { | |
703 | if ((config->hostname = malloc(strlen(ip)+1))) | |
704 | strcpy(config->hostname, ip); | |
705 | else | |
706 | config->flags &= ~CONFIG_NAME; | |
707 | } | |
708 | ||
709 | if (flags & CONFIG_ADDR) | |
710 | config->addr = addr; | |
1cff166d | 711 | } |
33820b7e | 712 | |
de37951c | 713 | config->flags |= CONFIG_HWADDR | CONFIG_NOCLID; |
33820b7e | 714 | memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN); |
cdeda28f SK |
715 | config->hwaddr_len = ETHER_ADDR_LEN; |
716 | config->hwaddr_type = ARPHRD_ETHER; | |
33820b7e | 717 | count++; |
44a2a316 SK |
718 | } |
719 | ||
720 | fclose(f); | |
33820b7e | 721 | |
b8187c80 | 722 | syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count); |
3be34541 SK |
723 | |
724 | daemon->dhcp_conf = configs; | |
44a2a316 SK |
725 | } |
726 | ||
727 | void dhcp_update_configs(struct dhcp_config *configs) | |
1ab84e2f | 728 | { |
44a2a316 SK |
729 | /* Some people like to keep all static IP addresses in /etc/hosts. |
730 | This goes through /etc/hosts and sets static addresses for any DHCP config | |
3d8df260 SK |
731 | records which don't have an address and whose name matches. |
732 | We take care to maintain the invariant that any IP address can appear | |
733 | in at most one dhcp-host. */ | |
44a2a316 | 734 | |
1ab84e2f SK |
735 | struct dhcp_config *config; |
736 | struct crec *crec; | |
44a2a316 | 737 | |
1ab84e2f | 738 | for (config = configs; config; config = config->next) |
33820b7e SK |
739 | if (!(config->flags & CONFIG_ADDR) && |
740 | (config->flags & CONFIG_NAME) && | |
1ab84e2f SK |
741 | (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) && |
742 | (crec->flags & F_HOSTS)) | |
33820b7e | 743 | { |
3d8df260 | 744 | if (config_find_by_address(configs, crec->addr.addr.addr.addr4)) |
b8187c80 | 745 | syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), |
3d8df260 SK |
746 | inet_ntoa(crec->addr.addr.addr.addr4), config->hostname); |
747 | else | |
748 | { | |
749 | config->addr = crec->addr.addr.addr.addr4; | |
750 | config->flags |= CONFIG_ADDR; | |
751 | } | |
33820b7e | 752 | } |
1ab84e2f | 753 | } |
44a2a316 | 754 | |
bb01cb96 SK |
755 | /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts |
756 | for this address. If it has a domain part, that must match the set domain and | |
757 | it gets stripped. */ | |
758 | char *host_from_dns(struct daemon *daemon, struct in_addr addr) | |
759 | { | |
760 | struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4); | |
761 | char *hostname = NULL; | |
762 | ||
763 | if (lookup && (lookup->flags & F_HOSTS)) | |
764 | { | |
765 | hostname = daemon->dhcp_buff; | |
bb01cb96 | 766 | strncpy(hostname, cache_get_name(lookup), 256); |
5e9e0efb | 767 | hostname[255] = 0; |
bb01cb96 SK |
768 | hostname = strip_hostname(daemon, hostname); |
769 | } | |
770 | ||
771 | return hostname; | |
772 | } | |
773 | ||
774 | char *strip_hostname(struct daemon *daemon, char *hostname) | |
775 | { | |
776 | char *dot = strchr(hostname, '.'); | |
777 | if (dot) | |
778 | { | |
779 | if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)) | |
780 | { | |
b8187c80 | 781 | syslog(LOG_WARNING, _("Ignoring DHCP host name %s because it has an illegal domain part"), hostname); |
bb01cb96 SK |
782 | hostname = NULL; |
783 | } | |
784 | else | |
785 | { | |
786 | *dot = 0; /* truncate */ | |
787 | if (strlen(hostname) == 0) | |
788 | hostname = NULL; /* nothing left */ | |
789 | } | |
790 | } | |
791 | return hostname; | |
792 | } |