]>
Commit | Line | Data |
---|---|---|
61744359 | 1 | /* dnsmasq is Copyright (c) 2000-2013 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 | |
824af85b SK |
5 | the Free Software Foundation; version 2 dated June, 1991, or |
6 | (at your option) version 3 dated 29 June, 2007. | |
7 | ||
9e4abcb5 SK |
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. | |
824af85b | 12 | |
73a08a24 SK |
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/>. | |
9e4abcb5 SK |
15 | */ |
16 | ||
9e4abcb5 SK |
17 | #include "dnsmasq.h" |
18 | ||
7622fc06 SK |
19 | #ifdef HAVE_DHCP |
20 | ||
5e9e0efb | 21 | struct iface_param { |
5e9e0efb SK |
22 | struct dhcp_context *current; |
23 | int ind; | |
24 | }; | |
25 | ||
c72daea8 SK |
26 | struct match_param { |
27 | int ind, matched; | |
28 | struct in_addr netmask, broadcast, addr; | |
29 | }; | |
30 | ||
5aabfc78 | 31 | static int complete_context(struct in_addr local, int if_index, |
5e9e0efb | 32 | struct in_addr netmask, struct in_addr broadcast, void *vparam); |
c72daea8 SK |
33 | static int check_listen_addrs(struct in_addr local, int if_index, |
34 | struct in_addr netmask, struct in_addr broadcast, void *vparam); | |
5e9e0efb | 35 | |
316e2730 | 36 | static int make_fd(int port) |
44a2a316 SK |
37 | { |
38 | int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
39 | struct sockaddr_in saddr; | |
7cebd20f | 40 | int oneopt = 1; |
824af85b SK |
41 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
42 | int mtu = IP_PMTUDISC_DONT; | |
43 | #endif | |
c72daea8 SK |
44 | #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6) |
45 | int tos = IPTOS_CLASS_CS6; | |
46 | #endif | |
dfa666f2 | 47 | |
44a2a316 | 48 | if (fd == -1) |
7622fc06 | 49 | die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET); |
44a2a316 | 50 | |
824af85b SK |
51 | if (!fix_fd(fd) || |
52 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) | |
c72daea8 SK |
53 | setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 || |
54 | #endif | |
55 | #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6) | |
56 | setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 || | |
824af85b | 57 | #endif |
5e9e0efb | 58 | #if defined(HAVE_LINUX_NETWORK) |
c72daea8 | 59 | setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 || |
7622fc06 | 60 | #else |
3be34541 | 61 | setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 || |
44a2a316 | 62 | #endif |
3be34541 | 63 | setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1) |
5aabfc78 | 64 | die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET); |
44a2a316 | 65 | |
f6b7dc47 | 66 | /* When bind-interfaces is set, there might be more than one dnmsasq |
4011c4e0 | 67 | instance binding port 67. That's OK if they serve different networks. |
56a1142f SK |
68 | Need to set REUSEADDR|REUSEPORT to make this posible. |
69 | Handle the case that REUSEPORT is defined, but the kernel doesn't | |
70 | support it. This handles the introduction of REUSEPORT on Linux. */ | |
54dd393f | 71 | if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) |
4011c4e0 | 72 | { |
56a1142f SK |
73 | int rc = -1, porterr = 0; |
74 | ||
4011c4e0 | 75 | #ifdef SO_REUSEPORT |
56a1142f SK |
76 | if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && |
77 | errno != ENOPROTOOPT) | |
78 | porterr = 1; | |
4011c4e0 | 79 | #endif |
56a1142f SK |
80 | |
81 | if (rc == -1 && !porterr) | |
82 | rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)); | |
83 | ||
4011c4e0 | 84 | if (rc == -1) |
5aabfc78 | 85 | die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET); |
4011c4e0 | 86 | } |
f6b7dc47 | 87 | |
849a8357 | 88 | memset(&saddr, 0, sizeof(saddr)); |
44a2a316 | 89 | saddr.sin_family = AF_INET; |
316e2730 | 90 | saddr.sin_port = htons(port); |
44a2a316 | 91 | saddr.sin_addr.s_addr = INADDR_ANY; |
3be34541 SK |
92 | #ifdef HAVE_SOCKADDR_SA_LEN |
93 | saddr.sin_len = sizeof(struct sockaddr_in); | |
94 | #endif | |
95 | ||
44a2a316 | 96 | if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in))) |
5aabfc78 | 97 | die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET); |
44a2a316 | 98 | |
316e2730 SK |
99 | return fd; |
100 | } | |
101 | ||
102 | void dhcp_init(void) | |
103 | { | |
104 | #if defined(HAVE_BSD_NETWORK) | |
105 | int oneopt = 1; | |
106 | #endif | |
107 | ||
108 | daemon->dhcpfd = make_fd(daemon->dhcp_server_port); | |
109 | if (daemon->enable_pxe) | |
110 | daemon->pxefd = make_fd(PXE_PORT); | |
111 | else | |
112 | daemon->pxefd = -1; | |
3be34541 | 113 | |
824af85b | 114 | #if defined(HAVE_BSD_NETWORK) |
5e9e0efb SK |
115 | /* When we're not using capabilities, we need to do this here before |
116 | we drop root. Also, set buffer size small, to avoid wasting | |
117 | kernel buffers */ | |
118 | ||
28866e95 | 119 | if (option_bool(OPT_NO_PING)) |
5e9e0efb SK |
120 | daemon->dhcp_icmp_fd = -1; |
121 | else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 || | |
122 | setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 ) | |
5aabfc78 | 123 | die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET); |
44a2a316 | 124 | |
5e9e0efb | 125 | /* Make BPF raw send socket */ |
5aabfc78 | 126 | init_bpf(); |
c4a7f90e | 127 | #endif |
c72daea8 SK |
128 | } |
129 | ||
316e2730 | 130 | void dhcp_packet(time_t now, int pxe_fd) |
9e4abcb5 | 131 | { |
316e2730 | 132 | int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd; |
5e9e0efb | 133 | struct dhcp_packet *mess; |
44a2a316 SK |
134 | struct dhcp_context *context; |
135 | struct iname *tmp; | |
136 | struct ifreq ifr; | |
137 | struct msghdr msg; | |
5e9e0efb | 138 | struct sockaddr_in dest; |
44a2a316 | 139 | struct cmsghdr *cmptr; |
5e9e0efb SK |
140 | struct iovec iov; |
141 | ssize_t sz; | |
5aabfc78 | 142 | int iface_index = 0, unicast_dest = 0, is_inform = 0; |
c72daea8 | 143 | struct in_addr iface_addr; |
5e9e0efb | 144 | struct iface_param parm; |
8ef5ada2 SK |
145 | #ifdef HAVE_LINUX_NETWORK |
146 | struct arpreq arp_req; | |
147 | #endif | |
148 | ||
44a2a316 SK |
149 | union { |
150 | struct cmsghdr align; /* this ensures alignment */ | |
824af85b | 151 | #if defined(HAVE_LINUX_NETWORK) |
44a2a316 | 152 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
824af85b SK |
153 | #elif defined(HAVE_SOLARIS_NETWORK) |
154 | char control[CMSG_SPACE(sizeof(unsigned int))]; | |
7622fc06 | 155 | #elif defined(HAVE_BSD_NETWORK) |
44a2a316 | 156 | char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; |
9e4abcb5 | 157 | #endif |
44a2a316 | 158 | } control_u; |
c72daea8 | 159 | struct dhcp_bridge *bridge, *alias; |
7622fc06 | 160 | |
5e9e0efb | 161 | msg.msg_controllen = sizeof(control_u); |
4011c4e0 | 162 | msg.msg_control = control_u.control; |
5e9e0efb SK |
163 | msg.msg_name = &dest; |
164 | msg.msg_namelen = sizeof(dest); | |
c72daea8 SK |
165 | msg.msg_iov = &daemon->dhcp_packet; |
166 | msg.msg_iovlen = 1; | |
44a2a316 | 167 | |
c72daea8 SK |
168 | if ((sz = recv_dhcp_packet(fd, &msg)) == -1 || |
169 | (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))) | |
170 | return; | |
171 | ||
172 | #if defined (HAVE_LINUX_NETWORK) | |
4011c4e0 SK |
173 | if (msg.msg_controllen >= sizeof(struct cmsghdr)) |
174 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
c72daea8 | 175 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) |
4011c4e0 | 176 | { |
8ef5ada2 SK |
177 | union { |
178 | unsigned char *c; | |
179 | struct in_pktinfo *p; | |
180 | } p; | |
181 | p.c = CMSG_DATA(cmptr); | |
182 | iface_index = p.p->ipi_ifindex; | |
183 | if (p.p->ipi_addr.s_addr != INADDR_BROADCAST) | |
4011c4e0 SK |
184 | unicast_dest = 1; |
185 | } | |
7622fc06 SK |
186 | |
187 | #elif defined(HAVE_BSD_NETWORK) | |
188 | if (msg.msg_controllen >= sizeof(struct cmsghdr)) | |
189 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
190 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) | |
8ef5ada2 SK |
191 | { |
192 | union { | |
193 | unsigned char *c; | |
194 | struct sockaddr_dl *s; | |
195 | } p; | |
196 | p.c = CMSG_DATA(cmptr); | |
197 | iface_index = p.s->sdl_index; | |
198 | } | |
4011c4e0 | 199 | |
7622fc06 | 200 | #elif defined(HAVE_SOLARIS_NETWORK) |
4011c4e0 SK |
201 | if (msg.msg_controllen >= sizeof(struct cmsghdr)) |
202 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
203 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) | |
8ef5ada2 SK |
204 | { |
205 | union { | |
206 | unsigned char *c; | |
207 | unsigned int *i; | |
208 | } p; | |
209 | p.c = CMSG_DATA(cmptr); | |
210 | iface_index = *(p.i); | |
211 | } | |
7622fc06 SK |
212 | #endif |
213 | ||
214 | if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name)) | |
44a2a316 | 215 | return; |
7622fc06 | 216 | |
8ef5ada2 SK |
217 | #ifdef HAVE_LINUX_NETWORK |
218 | /* ARP fiddling uses original interface even if we pretend to use a different one. */ | |
219 | strncpy(arp_req.arp_dev, ifr.ifr_name, 16); | |
220 | #endif | |
221 | ||
c72daea8 SK |
222 | /* One form of bridging on BSD has the property that packets |
223 | can be recieved on bridge interfaces which do not have an IP address. | |
224 | We allow these to be treated as aliases of another interface which does have | |
225 | an IP address with --dhcp-bridge=interface,alias,alias */ | |
226 | for (bridge = daemon->bridges; bridge; bridge = bridge->next) | |
227 | { | |
228 | for (alias = bridge->alias; alias; alias = alias->next) | |
229 | if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0) | |
230 | { | |
231 | if (!(iface_index = if_nametoindex(bridge->iface))) | |
232 | { | |
233 | my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name); | |
234 | return; | |
235 | } | |
236 | else | |
237 | { | |
238 | strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE); | |
239 | break; | |
240 | } | |
241 | } | |
242 | ||
243 | if (alias) | |
244 | break; | |
245 | } | |
246 | ||
4011c4e0 SK |
247 | #ifdef MSG_BCAST |
248 | /* OpenBSD tells us when a packet was broadcast */ | |
249 | if (!(msg.msg_flags & MSG_BCAST)) | |
250 | unicast_dest = 1; | |
251 | #endif | |
c72daea8 | 252 | |
44a2a316 | 253 | ifr.ifr_addr.sa_family = AF_INET; |
832af0ba | 254 | if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 ) |
c72daea8 SK |
255 | iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; |
256 | else | |
832af0ba | 257 | { |
c72daea8 SK |
258 | my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name); |
259 | return; | |
832af0ba | 260 | } |
832af0ba | 261 | |
3d8df260 | 262 | for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) |
49333cbd | 263 | if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) |
3d8df260 | 264 | return; |
c72daea8 | 265 | |
91dccd09 | 266 | /* unlinked contexts are marked by context->current == context */ |
3be34541 | 267 | for (context = daemon->dhcp; context; context = context->next) |
91dccd09 | 268 | context->current = context; |
0a852541 | 269 | |
5e9e0efb SK |
270 | parm.current = NULL; |
271 | parm.ind = iface_index; | |
0a852541 | 272 | |
4f7b304f | 273 | if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL)) |
8ef5ada2 | 274 | { |
c72daea8 SK |
275 | /* If we failed to match the primary address of the interface, see if we've got a --listen-address |
276 | for a secondary */ | |
277 | struct match_param match; | |
8bc4cece | 278 | |
c72daea8 SK |
279 | match.matched = 0; |
280 | match.ind = iface_index; | |
281 | ||
282 | if (!daemon->if_addrs || | |
283 | !iface_enumerate(AF_INET, &match, check_listen_addrs) || | |
284 | !match.matched) | |
285 | return; | |
8ef5ada2 | 286 | |
c72daea8 SK |
287 | iface_addr = match.addr; |
288 | /* make sure secondary address gets priority in case | |
289 | there is more than one address on the interface in the same subnet */ | |
290 | complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm); | |
291 | } | |
292 | ||
28866e95 | 293 | if (!iface_enumerate(AF_INET, &parm, complete_context)) |
5e9e0efb | 294 | return; |
c72daea8 | 295 | |
44a2a316 | 296 | lease_prune(NULL, now); /* lose any expired leases */ |
824af85b | 297 | iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, |
7de060b0 | 298 | now, unicast_dest, &is_inform, pxe_fd, iface_addr); |
5aabfc78 | 299 | lease_update_file(now); |
353ae4d2 | 300 | lease_update_dns(0); |
1697269c | 301 | |
5e9e0efb | 302 | if (iov.iov_len == 0) |
44a2a316 SK |
303 | return; |
304 | ||
5e9e0efb SK |
305 | msg.msg_name = &dest; |
306 | msg.msg_namelen = sizeof(dest); | |
307 | msg.msg_control = NULL; | |
308 | msg.msg_controllen = 0; | |
309 | msg.msg_iov = &iov; | |
310 | iov.iov_base = daemon->dhcp_packet.iov_base; | |
311 | ||
312 | /* packet buffer may have moved */ | |
824af85b | 313 | mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; |
5e9e0efb | 314 | |
3be34541 | 315 | #ifdef HAVE_SOCKADDR_SA_LEN |
5e9e0efb | 316 | dest.sin_len = sizeof(struct sockaddr_in); |
3be34541 | 317 | #endif |
c72daea8 | 318 | |
316e2730 SK |
319 | if (pxe_fd) |
320 | { | |
321 | if (mess->ciaddr.s_addr != 0) | |
322 | dest.sin_addr = mess->ciaddr; | |
323 | } | |
324 | else if (mess->giaddr.s_addr) | |
5e9e0efb SK |
325 | { |
326 | /* Send to BOOTP relay */ | |
9e038946 | 327 | dest.sin_port = htons(daemon->dhcp_server_port); |
5e9e0efb SK |
328 | dest.sin_addr = mess->giaddr; |
329 | } | |
330 | else if (mess->ciaddr.s_addr) | |
331 | { | |
208b65c5 SK |
332 | /* If the client's idea of its own address tallys with |
333 | the source address in the request packet, we believe the | |
5aabfc78 SK |
334 | source port too, and send back to that. If we're replying |
335 | to a DHCPINFORM, trust the source address always. */ | |
336 | if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) || | |
337 | dest.sin_port == 0 || dest.sin_addr.s_addr == 0) | |
208b65c5 | 338 | { |
9e038946 | 339 | dest.sin_port = htons(daemon->dhcp_client_port); |
208b65c5 SK |
340 | dest.sin_addr = mess->ciaddr; |
341 | } | |
5e9e0efb | 342 | } |
824af85b | 343 | #if defined(HAVE_LINUX_NETWORK) |
849a8357 SK |
344 | else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 || |
345 | mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0) | |
5e9e0efb | 346 | { |
849a8357 | 347 | /* broadcast to 255.255.255.255 (or mac address invalid) */ |
5e9e0efb | 348 | struct in_pktinfo *pkt; |
26d0dbaf | 349 | msg.msg_control = control_u.control; |
5e9e0efb SK |
350 | msg.msg_controllen = sizeof(control_u); |
351 | cmptr = CMSG_FIRSTHDR(&msg); | |
5e9e0efb SK |
352 | pkt = (struct in_pktinfo *)CMSG_DATA(cmptr); |
353 | pkt->ipi_ifindex = iface_index; | |
354 | pkt->ipi_spec_dst.s_addr = 0; | |
355 | msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); | |
c72daea8 | 356 | cmptr->cmsg_level = IPPROTO_IP; |
824af85b SK |
357 | cmptr->cmsg_type = IP_PKTINFO; |
358 | dest.sin_addr.s_addr = INADDR_BROADCAST; | |
9e038946 | 359 | dest.sin_port = htons(daemon->dhcp_client_port); |
44a2a316 SK |
360 | } |
361 | else | |
362 | { | |
849a8357 SK |
363 | /* unicast to unconfigured client. Inject mac address direct into ARP cache. |
364 | struct sockaddr limits size to 14 bytes. */ | |
5e9e0efb | 365 | dest.sin_addr = mess->yiaddr; |
9e038946 | 366 | dest.sin_port = htons(daemon->dhcp_client_port); |
8ef5ada2 SK |
367 | memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in)); |
368 | arp_req.arp_ha.sa_family = mess->htype; | |
369 | memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen); | |
370 | /* interface name already copied in */ | |
371 | arp_req.arp_flags = ATF_COM; | |
372 | ioctl(daemon->dhcpfd, SIOCSARP, &arp_req); | |
5e9e0efb | 373 | } |
824af85b SK |
374 | #elif defined(HAVE_SOLARIS_NETWORK) |
375 | else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER) | |
376 | { | |
377 | /* broadcast to 255.255.255.255 (or mac address invalid) */ | |
378 | dest.sin_addr.s_addr = INADDR_BROADCAST; | |
9e038946 | 379 | dest.sin_port = htons(daemon->dhcp_client_port); |
824af85b | 380 | /* note that we don't specify the interface here: that's done by the |
7622fc06 | 381 | IP_BOUND_IF sockopt lower down. */ |
824af85b SK |
382 | } |
383 | else | |
384 | { | |
385 | /* unicast to unconfigured client. Inject mac address direct into ARP cache. | |
386 | Note that this only works for ethernet on solaris, because we use SIOCSARP | |
387 | and not SIOCSXARP, which would be perfect, except that it returns ENXIO | |
388 | mysteriously. Bah. Fall back to broadcast for other net types. */ | |
389 | struct arpreq req; | |
390 | dest.sin_addr = mess->yiaddr; | |
9e038946 | 391 | dest.sin_port = htons(daemon->dhcp_client_port); |
824af85b SK |
392 | *((struct sockaddr_in *)&req.arp_pa) = dest; |
393 | req.arp_ha.sa_family = AF_UNSPEC; | |
394 | memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen); | |
395 | req.arp_flags = ATF_COM; | |
396 | ioctl(daemon->dhcpfd, SIOCSARP, &req); | |
397 | } | |
398 | #elif defined(HAVE_BSD_NETWORK) | |
5e9e0efb SK |
399 | else |
400 | { | |
5aabfc78 | 401 | send_via_bpf(mess, iov.iov_len, iface_addr, &ifr); |
5e9e0efb | 402 | return; |
9e4abcb5 | 403 | } |
5e9e0efb SK |
404 | #endif |
405 | ||
824af85b | 406 | #ifdef HAVE_SOLARIS_NETWORK |
316e2730 | 407 | setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index)); |
824af85b SK |
408 | #endif |
409 | ||
316e2730 | 410 | while(sendmsg(fd, &msg, 0) == -1 && retry_send()); |
9e4abcb5 | 411 | } |
5e9e0efb | 412 | |
c72daea8 SK |
413 | /* check against secondary interface addresses */ |
414 | static int check_listen_addrs(struct in_addr local, int if_index, | |
415 | struct in_addr netmask, struct in_addr broadcast, void *vparam) | |
416 | { | |
417 | struct match_param *param = vparam; | |
418 | struct iname *tmp; | |
419 | ||
420 | if (if_index == param->ind) | |
421 | { | |
422 | for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) | |
423 | if ( tmp->addr.sa.sa_family == AF_INET && | |
424 | tmp->addr.in.sin_addr.s_addr == local.s_addr) | |
425 | { | |
426 | param->matched = 1; | |
427 | param->addr = local; | |
428 | param->netmask = netmask; | |
429 | param->broadcast = broadcast; | |
430 | break; | |
431 | } | |
432 | } | |
433 | ||
434 | return 1; | |
435 | } | |
436 | ||
0a852541 | 437 | /* This is a complex routine: it gets called with each (address,netmask,broadcast) triple |
5e9e0efb SK |
438 | of each interface (and any relay address) and does the following things: |
439 | ||
440 | 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived. | |
441 | 2) Fills in any netmask and broadcast addresses which have not been explicitly configured. | |
442 | 3) Fills in local (this host) and router (this host or relay) addresses. | |
443 | 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current. | |
444 | ||
0a852541 | 445 | Note that the current chain may be superceded later for configured hosts or those coming via gateways. */ |
5e9e0efb | 446 | |
5aabfc78 SK |
447 | static int complete_context(struct in_addr local, int if_index, |
448 | struct in_addr netmask, struct in_addr broadcast, void *vparam) | |
0a852541 SK |
449 | { |
450 | struct dhcp_context *context; | |
5e9e0efb SK |
451 | struct iface_param *param = vparam; |
452 | ||
0a852541 SK |
453 | for (context = daemon->dhcp; context; context = context->next) |
454 | { | |
455 | if (!(context->flags & CONTEXT_NETMASK) && | |
456 | (is_same_net(local, context->start, netmask) || | |
457 | is_same_net(local, context->end, netmask))) | |
458 | { | |
459 | if (context->netmask.s_addr != netmask.s_addr && | |
460 | !(is_same_net(local, context->start, netmask) && | |
461 | is_same_net(local, context->end, netmask))) | |
462 | { | |
463 | strcpy(daemon->dhcp_buff, inet_ntoa(context->start)); | |
464 | strcpy(daemon->dhcp_buff2, inet_ntoa(context->end)); | |
7622fc06 | 465 | my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"), |
f2621c7f | 466 | daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask)); |
0a852541 SK |
467 | } |
468 | context->netmask = netmask; | |
469 | } | |
470 | ||
7de060b0 SK |
471 | if (context->netmask.s_addr != 0 && |
472 | is_same_net(local, context->start, context->netmask) && | |
473 | is_same_net(local, context->end, context->netmask)) | |
0a852541 | 474 | { |
7de060b0 SK |
475 | /* link it onto the current chain if we've not seen it before */ |
476 | if (if_index == param->ind && context->current == context) | |
0a852541 | 477 | { |
7de060b0 SK |
478 | context->router = local; |
479 | context->local = local; | |
480 | context->current = param->current; | |
481 | param->current = context; | |
482 | } | |
483 | ||
484 | if (!(context->flags & CONTEXT_BRDCAST)) | |
0a852541 | 485 | { |
7de060b0 SK |
486 | if (is_same_net(broadcast, context->start, context->netmask)) |
487 | context->broadcast = broadcast; | |
488 | else | |
0a852541 SK |
489 | context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; |
490 | } | |
7de060b0 | 491 | } |
0a852541 SK |
492 | } |
493 | ||
5e9e0efb | 494 | return 1; |
0a852541 SK |
495 | } |
496 | ||
824af85b SK |
497 | struct dhcp_context *address_available(struct dhcp_context *context, |
498 | struct in_addr taddr, | |
499 | struct dhcp_netid *netids) | |
9e4abcb5 | 500 | { |
36717eee | 501 | /* Check is an address is OK for this network, check all |
5e9e0efb SK |
502 | possible ranges. Make sure that the address isn't in use |
503 | by the server itself. */ | |
9e4abcb5 | 504 | |
36717eee | 505 | unsigned int start, end, addr = ntohl(taddr.s_addr); |
5e9e0efb SK |
506 | struct dhcp_context *tmp; |
507 | ||
508 | for (tmp = context; tmp; tmp = tmp->current) | |
849a8357 | 509 | if (taddr.s_addr == context->router.s_addr) |
5e9e0efb | 510 | return NULL; |
9e4abcb5 | 511 | |
5e9e0efb | 512 | for (tmp = context; tmp; tmp = tmp->current) |
36717eee | 513 | { |
5e9e0efb SK |
514 | start = ntohl(tmp->start.s_addr); |
515 | end = ntohl(tmp->end.s_addr); | |
9e4abcb5 | 516 | |
7de060b0 | 517 | if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) && |
36717eee | 518 | addr >= start && |
824af85b SK |
519 | addr <= end && |
520 | match_netid(tmp->filter, netids, 1)) | |
5e9e0efb | 521 | return tmp; |
36717eee | 522 | } |
9e4abcb5 | 523 | |
59353a6b | 524 | return NULL; |
9e4abcb5 | 525 | } |
59353a6b | 526 | |
824af85b SK |
527 | struct dhcp_context *narrow_context(struct dhcp_context *context, |
528 | struct in_addr taddr, | |
529 | struct dhcp_netid *netids) | |
59353a6b | 530 | { |
e17fb629 | 531 | /* We start of with a set of possible contexts, all on the current physical interface. |
59353a6b SK |
532 | These are chained on ->current. |
533 | Here we have an address, and return the actual context correponding to that | |
534 | address. Note that none may fit, if the address came a dhcp-host and is outside | |
e17fb629 SK |
535 | any dhcp-range. In that case we return a static range if possible, or failing that, |
536 | any context on the correct subnet. (If there's more than one, this is a dodgy | |
537 | configuration: maybe there should be a warning.) */ | |
59353a6b | 538 | |
e17fb629 | 539 | struct dhcp_context *tmp; |
59353a6b | 540 | |
824af85b SK |
541 | if (!(tmp = address_available(context, taddr, netids))) |
542 | { | |
543 | for (tmp = context; tmp; tmp = tmp->current) | |
8ef5ada2 SK |
544 | if (match_netid(tmp->filter, netids, 1) && |
545 | is_same_net(taddr, tmp->start, tmp->netmask) && | |
7622fc06 SK |
546 | (tmp->flags & CONTEXT_STATIC)) |
547 | break; | |
824af85b SK |
548 | |
549 | if (!tmp) | |
550 | for (tmp = context; tmp; tmp = tmp->current) | |
8ef5ada2 | 551 | if (match_netid(tmp->filter, netids, 1) && |
7de060b0 SK |
552 | is_same_net(taddr, tmp->start, tmp->netmask) && |
553 | !(tmp->flags & CONTEXT_PROXY)) | |
824af85b SK |
554 | break; |
555 | } | |
59353a6b | 556 | |
824af85b SK |
557 | /* Only one context allowed now */ |
558 | if (tmp) | |
559 | tmp->current = NULL; | |
560 | ||
561 | return tmp; | |
59353a6b SK |
562 | } |
563 | ||
dfa666f2 SK |
564 | struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr) |
565 | { | |
566 | struct dhcp_config *config; | |
567 | ||
568 | for (config = configs; config; config = config->next) | |
569 | if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) | |
570 | return config; | |
571 | ||
572 | return NULL; | |
573 | } | |
9e4abcb5 | 574 | |
5aabfc78 | 575 | int address_allocate(struct dhcp_context *context, |
cdeda28f | 576 | struct in_addr *addrp, unsigned char *hwaddr, int hw_len, |
3d8df260 | 577 | struct dhcp_netid *netids, time_t now) |
9e4abcb5 | 578 | { |
feba5c1d | 579 | /* Find a free address: exclude anything in use and anything allocated to |
f6b7dc47 | 580 | a particular hwaddr/clientid/hostname in our configuration. |
cdeda28f | 581 | Try to return from contexts which match netids first. */ |
9e4abcb5 | 582 | |
5e9e0efb SK |
583 | struct in_addr start, addr; |
584 | struct dhcp_context *c, *d; | |
cdeda28f SK |
585 | int i, pass; |
586 | unsigned int j; | |
587 | ||
8ef5ada2 SK |
588 | /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good |
589 | dispersal even with similarly-valued "strings". */ | |
5e9e0efb | 590 | for (j = 0, i = 0; i < hw_len; i++) |
8ef5ada2 | 591 | j += hwaddr[i] + (j << 6) + (j << 16) - j; |
5e9e0efb | 592 | |
cdeda28f SK |
593 | for (pass = 0; pass <= 1; pass++) |
594 | for (c = context; c; c = c->current) | |
7de060b0 | 595 | if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) |
cdeda28f SK |
596 | continue; |
597 | else if (!match_netid(c->filter, netids, pass)) | |
598 | continue; | |
599 | else | |
600 | { | |
7de060b0 SK |
601 | if (option_bool(OPT_CONSEC_ADDR)) |
602 | /* seed is largest extant lease addr in this context */ | |
603 | start = lease_find_max_addr(c); | |
604 | else | |
605 | /* pick a seed based on hwaddr */ | |
606 | start.s_addr = htonl(ntohl(c->start.s_addr) + | |
607 | ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr)))); | |
608 | ||
609 | /* iterate until we find a free address. */ | |
610 | addr = start; | |
cdeda28f SK |
611 | |
612 | do { | |
5e9e0efb SK |
613 | /* eliminate addresses in use by the server. */ |
614 | for (d = context; d; d = d->current) | |
849a8357 | 615 | if (addr.s_addr == d->router.s_addr) |
5e9e0efb SK |
616 | break; |
617 | ||
73a08a24 SK |
618 | /* Addresses which end in .255 and .0 are broken in Windows even when using |
619 | supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0 | |
620 | then 192.168.0.255 is a valid IP address, but not for Windows as it's | |
621 | in the class C range. See KB281579. We therefore don't allocate these | |
622 | addresses to avoid hard-to-diagnose problems. Thanks Bill. */ | |
5e9e0efb SK |
623 | if (!d && |
624 | !lease_find_by_addr(addr) && | |
73a08a24 SK |
625 | !config_find_by_address(daemon->dhcp_conf, addr) && |
626 | (!IN_CLASSC(ntohl(addr.s_addr)) || | |
627 | ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0)))) | |
cdeda28f SK |
628 | { |
629 | struct ping_result *r, *victim = NULL; | |
5e9e0efb SK |
630 | int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/ |
631 | ((float)PING_WAIT))); | |
632 | ||
633 | *addrp = addr; | |
634 | ||
cdeda28f | 635 | /* check if we failed to ping addr sometime in the last |
5e9e0efb | 636 | PING_CACHE_TIME seconds. If so, assume the same situation still exists. |
cdeda28f | 637 | This avoids problems when a stupid client bangs |
5e9e0efb SK |
638 | on us repeatedly. As a final check, if we did more |
639 | than 60% of the possible ping checks in the last | |
640 | PING_CACHE_TIME, we are in high-load mode, so don't do any more. */ | |
cdeda28f | 641 | for (count = 0, r = daemon->ping_results; r; r = r->next) |
5e9e0efb | 642 | if (difftime(now, r->time) > (float)PING_CACHE_TIME) |
cdeda28f | 643 | victim = r; /* old record */ |
7de060b0 SK |
644 | else |
645 | { | |
646 | count++; | |
647 | if (r->addr.s_addr == addr.s_addr) | |
648 | { | |
649 | /* consec-ip mode: we offered this address for another client | |
650 | (different hash) recently, don't offer it to this one. */ | |
651 | if (option_bool(OPT_CONSEC_ADDR) && r->hash != j) | |
652 | break; | |
653 | ||
654 | return 1; | |
655 | } | |
656 | } | |
657 | ||
658 | if (!r) | |
3d8df260 | 659 | { |
7de060b0 | 660 | if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr)) |
cdeda28f | 661 | { |
7de060b0 SK |
662 | /* address in use: perturb address selection so that we are |
663 | less likely to try this address again. */ | |
664 | if (!option_bool(OPT_CONSEC_ADDR)) | |
665 | c->addr_epoch++; | |
cdeda28f | 666 | } |
7de060b0 | 667 | else |
cdeda28f | 668 | { |
7de060b0 SK |
669 | /* at this point victim may hold an expired record */ |
670 | if (!victim) | |
671 | { | |
672 | if ((victim = whine_malloc(sizeof(struct ping_result)))) | |
673 | { | |
674 | victim->next = daemon->ping_results; | |
675 | daemon->ping_results = victim; | |
676 | } | |
677 | } | |
678 | ||
679 | /* record that this address is OK for 30s | |
680 | without more ping checks */ | |
681 | if (victim) | |
682 | { | |
683 | victim->addr = addr; | |
684 | victim->time = now; | |
685 | victim->hash = j; | |
686 | } | |
687 | return 1; | |
cdeda28f | 688 | } |
3d8df260 | 689 | } |
cdeda28f SK |
690 | } |
691 | ||
692 | addr.s_addr = htonl(ntohl(addr.s_addr) + 1); | |
693 | ||
694 | if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1)) | |
695 | addr = c->start; | |
696 | ||
697 | } while (addr.s_addr != start.s_addr); | |
698 | } | |
7de060b0 | 699 | |
9e4abcb5 SK |
700 | return 0; |
701 | } | |
702 | ||
703 | static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config) | |
704 | { | |
b8187c80 | 705 | if (!context) /* called via find_config() from lease_update_from_configs() */ |
0a852541 | 706 | return 1; |
33820b7e | 707 | if (!(config->flags & CONFIG_ADDR)) |
9e4abcb5 | 708 | return 1; |
b8187c80 SK |
709 | for (; context; context = context->current) |
710 | if (is_same_net(config->addr, context->start, context->netmask)) | |
711 | return 1; | |
9e4abcb5 SK |
712 | |
713 | return 0; | |
714 | } | |
715 | ||
9009d746 SK |
716 | int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type) |
717 | { | |
718 | struct hwaddr_config *conf_addr; | |
719 | ||
720 | for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) | |
721 | if (conf_addr->wildcard_mask == 0 && | |
722 | conf_addr->hwaddr_len == len && | |
723 | (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) && | |
724 | memcmp(conf_addr->hwaddr, hwaddr, len) == 0) | |
725 | return 1; | |
726 | ||
727 | return 0; | |
728 | } | |
0a852541 | 729 | |
9e4abcb5 SK |
730 | struct dhcp_config *find_config(struct dhcp_config *configs, |
731 | struct dhcp_context *context, | |
732 | unsigned char *clid, int clid_len, | |
cdeda28f SK |
733 | unsigned char *hwaddr, int hw_len, |
734 | int hw_type, char *hostname) | |
9e4abcb5 | 735 | { |
7622fc06 SK |
736 | int count, new; |
737 | struct dhcp_config *config, *candidate; | |
9009d746 SK |
738 | struct hwaddr_config *conf_addr; |
739 | ||
0a852541 | 740 | if (clid) |
9e4abcb5 | 741 | for (config = configs; config; config = config->next) |
33820b7e SK |
742 | if (config->flags & CONFIG_CLID) |
743 | { | |
744 | if (config->clid_len == clid_len && | |
745 | memcmp(config->clid, clid, clid_len) == 0 && | |
746 | is_addr_in_context(context, config)) | |
747 | return config; | |
748 | ||
749 | /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and | |
750 | cope with that here */ | |
751 | if (*clid == 0 && config->clid_len == clid_len-1 && | |
752 | memcmp(config->clid, clid+1, clid_len-1) == 0 && | |
753 | is_addr_in_context(context, config)) | |
754 | return config; | |
755 | } | |
756 | ||
0a852541 | 757 | |
cdeda28f | 758 | for (config = configs; config; config = config->next) |
9009d746 | 759 | if (config_has_mac(config, hwaddr, hw_len, hw_type) && |
cdeda28f SK |
760 | is_addr_in_context(context, config)) |
761 | return config; | |
3d8df260 | 762 | |
0a852541 | 763 | if (hostname && context) |
9e4abcb5 | 764 | for (config = configs; config; config = config->next) |
33820b7e SK |
765 | if ((config->flags & CONFIG_NAME) && |
766 | hostname_isequal(config->hostname, hostname) && | |
9e4abcb5 SK |
767 | is_addr_in_context(context, config)) |
768 | return config; | |
7622fc06 | 769 | |
4cb1b320 | 770 | /* use match with fewest wildcard octets */ |
7622fc06 SK |
771 | for (candidate = NULL, count = 0, config = configs; config; config = config->next) |
772 | if (is_addr_in_context(context, config)) | |
773 | for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) | |
774 | if (conf_addr->wildcard_mask != 0 && | |
775 | conf_addr->hwaddr_len == hw_len && | |
776 | (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) && | |
777 | (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count) | |
778 | { | |
779 | count = new; | |
780 | candidate = config; | |
781 | } | |
782 | ||
783 | return candidate; | |
9e4abcb5 SK |
784 | } |
785 | ||
5aabfc78 | 786 | void dhcp_read_ethers(void) |
44a2a316 SK |
787 | { |
788 | FILE *f = fopen(ETHERSFILE, "r"); | |
0a852541 | 789 | unsigned int flags; |
3be34541 | 790 | char *buff = daemon->namebuff; |
33820b7e | 791 | char *ip, *cp; |
44a2a316 | 792 | struct in_addr addr; |
33820b7e | 793 | unsigned char hwaddr[ETHER_ADDR_LEN]; |
849a8357 | 794 | struct dhcp_config **up, *tmp; |
1697269c | 795 | struct dhcp_config *config; |
b8187c80 | 796 | int count = 0, lineno = 0; |
3d8df260 SK |
797 | |
798 | addr.s_addr = 0; /* eliminate warning */ | |
44a2a316 SK |
799 | |
800 | if (!f) | |
33820b7e | 801 | { |
7622fc06 | 802 | my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno)); |
3be34541 | 803 | return; |
33820b7e SK |
804 | } |
805 | ||
849a8357 | 806 | /* This can be called again on SIGHUP, so remove entries created last time round. */ |
1697269c | 807 | for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp) |
849a8357 SK |
808 | { |
809 | tmp = config->next; | |
810 | if (config->flags & CONFIG_FROM_ETHERS) | |
811 | { | |
812 | *up = tmp; | |
813 | /* cannot have a clid */ | |
814 | if (config->flags & CONFIG_NAME) | |
815 | free(config->hostname); | |
9009d746 | 816 | free(config->hwaddr); |
849a8357 SK |
817 | free(config); |
818 | } | |
819 | else | |
820 | up = &config->next; | |
821 | } | |
822 | ||
44a2a316 SK |
823 | while (fgets(buff, MAXDNAME, f)) |
824 | { | |
1f15b81d SK |
825 | char *host = NULL; |
826 | ||
b8187c80 SK |
827 | lineno++; |
828 | ||
824af85b | 829 | while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1])) |
44a2a316 SK |
830 | buff[strlen(buff)-1] = 0; |
831 | ||
73a08a24 | 832 | if ((*buff == '#') || (*buff == '+') || (*buff == 0)) |
44a2a316 SK |
833 | continue; |
834 | ||
824af85b SK |
835 | for (ip = buff; *ip && !isspace((int)*ip); ip++); |
836 | for(; *ip && isspace((int)*ip); ip++) | |
44a2a316 | 837 | *ip = 0; |
cdeda28f | 838 | if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN) |
b8187c80 | 839 | { |
7622fc06 | 840 | my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno); |
b8187c80 SK |
841 | continue; |
842 | } | |
44a2a316 SK |
843 | |
844 | /* check for name or dotted-quad */ | |
845 | for (cp = ip; *cp; cp++) | |
846 | if (!(*cp == '.' || (*cp >='0' && *cp <= '9'))) | |
847 | break; | |
848 | ||
849 | if (!*cp) | |
850 | { | |
44a2a316 | 851 | if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1) |
b8187c80 | 852 | { |
7622fc06 | 853 | my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno); |
b8187c80 SK |
854 | continue; |
855 | } | |
856 | ||
33820b7e | 857 | flags = CONFIG_ADDR; |
1cff166d | 858 | |
1697269c | 859 | for (config = daemon->dhcp_conf; config; config = config->next) |
33820b7e | 860 | if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) |
1cff166d | 861 | break; |
44a2a316 SK |
862 | } |
863 | else | |
864 | { | |
1f15b81d SK |
865 | int nomem; |
866 | if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host)) | |
b8187c80 | 867 | { |
1f15b81d SK |
868 | if (!nomem) |
869 | my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno); | |
870 | free(host); | |
b8187c80 SK |
871 | continue; |
872 | } | |
5aabfc78 | 873 | |
33820b7e | 874 | flags = CONFIG_NAME; |
1cff166d | 875 | |
1697269c | 876 | for (config = daemon->dhcp_conf; config; config = config->next) |
1f15b81d | 877 | if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host)) |
1cff166d | 878 | break; |
44a2a316 | 879 | } |
1f15b81d SK |
880 | |
881 | if (config && (config->flags & CONFIG_FROM_ETHERS)) | |
882 | { | |
883 | my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno); | |
884 | continue; | |
885 | } | |
886 | ||
1cff166d SK |
887 | if (!config) |
888 | { | |
1697269c | 889 | for (config = daemon->dhcp_conf; config; config = config->next) |
9009d746 SK |
890 | { |
891 | struct hwaddr_config *conf_addr = config->hwaddr; | |
892 | if (conf_addr && | |
893 | conf_addr->next == NULL && | |
894 | conf_addr->wildcard_mask == 0 && | |
895 | conf_addr->hwaddr_len == ETHER_ADDR_LEN && | |
896 | (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) && | |
897 | memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0) | |
898 | break; | |
899 | } | |
33820b7e SK |
900 | |
901 | if (!config) | |
902 | { | |
5aabfc78 | 903 | if (!(config = whine_malloc(sizeof(struct dhcp_config)))) |
33820b7e | 904 | continue; |
849a8357 | 905 | config->flags = CONFIG_FROM_ETHERS; |
9009d746 SK |
906 | config->hwaddr = NULL; |
907 | config->domain = NULL; | |
c52e1897 | 908 | config->netid = NULL; |
1697269c SK |
909 | config->next = daemon->dhcp_conf; |
910 | daemon->dhcp_conf = config; | |
33820b7e SK |
911 | } |
912 | ||
913 | config->flags |= flags; | |
914 | ||
915 | if (flags & CONFIG_NAME) | |
916 | { | |
1f15b81d SK |
917 | config->hostname = host; |
918 | host = NULL; | |
33820b7e SK |
919 | } |
920 | ||
921 | if (flags & CONFIG_ADDR) | |
922 | config->addr = addr; | |
1cff166d | 923 | } |
33820b7e | 924 | |
9009d746 SK |
925 | config->flags |= CONFIG_NOCLID; |
926 | if (!config->hwaddr) | |
927 | config->hwaddr = whine_malloc(sizeof(struct hwaddr_config)); | |
928 | if (config->hwaddr) | |
929 | { | |
930 | memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN); | |
931 | config->hwaddr->hwaddr_len = ETHER_ADDR_LEN; | |
932 | config->hwaddr->hwaddr_type = ARPHRD_ETHER; | |
933 | config->hwaddr->wildcard_mask = 0; | |
934 | config->hwaddr->next = NULL; | |
935 | } | |
33820b7e | 936 | count++; |
1f15b81d SK |
937 | |
938 | free(host); | |
939 | ||
44a2a316 SK |
940 | } |
941 | ||
942 | fclose(f); | |
33820b7e | 943 | |
7622fc06 | 944 | my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count); |
44a2a316 SK |
945 | } |
946 | ||
44a2a316 | 947 | |
bb01cb96 SK |
948 | /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts |
949 | for this address. If it has a domain part, that must match the set domain and | |
1f15b81d | 950 | it gets stripped. The set of legal domain names is bigger than the set of legal hostnames |
8ef5ada2 SK |
951 | so check here that the domain name is legal as a hostname. |
952 | NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */ | |
5aabfc78 | 953 | char *host_from_dns(struct in_addr addr) |
bb01cb96 | 954 | { |
824af85b | 955 | struct crec *lookup; |
824af85b SK |
956 | |
957 | if (daemon->port == 0) | |
958 | return NULL; /* DNS disabled. */ | |
bb01cb96 | 959 | |
824af85b | 960 | lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4); |
8ef5ada2 | 961 | |
bb01cb96 SK |
962 | if (lookup && (lookup->flags & F_HOSTS)) |
963 | { | |
8ef5ada2 SK |
964 | char *dot, *hostname = cache_get_name(lookup); |
965 | dot = strchr(hostname, '.'); | |
966 | ||
967 | if (dot && strlen(dot+1) != 0) | |
968 | { | |
969 | char *d2 = get_domain(addr); | |
970 | if (!d2 || !hostname_isequal(dot+1, d2)) | |
971 | return NULL; /* wrong domain */ | |
972 | } | |
973 | ||
974 | if (!legal_hostname(hostname)) | |
975 | return NULL; | |
976 | ||
977 | strncpy(daemon->dhcp_buff, hostname, 256); | |
978 | daemon->dhcp_buff[255] = 0; | |
979 | strip_hostname(daemon->dhcp_buff); | |
980 | ||
981 | return daemon->dhcp_buff; | |
bb01cb96 | 982 | } |
5aabfc78 | 983 | |
8ef5ada2 | 984 | return NULL; |
bb01cb96 SK |
985 | } |
986 | ||
7622fc06 | 987 | #endif |
9009d746 | 988 |