]>
Commit | Line | Data |
---|---|---|
9e4abcb5 SK |
1 | /* dnsmasq is Copyright (c) 2000-2003 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. | |
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 | ||
13 | /* Author's email: simon@thekelleys.org.uk */ | |
14 | ||
15 | #include "dnsmasq.h" | |
16 | ||
3be34541 | 17 | void dhcp_init(struct daemon *daemon) |
44a2a316 SK |
18 | { |
19 | int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
20 | struct sockaddr_in saddr; | |
fd9fa481 | 21 | int flags, oneopt = 1, zeroopt = 0; |
3be34541 | 22 | struct dhcp_config *configs, *cp; |
dfa666f2 | 23 | |
44a2a316 | 24 | if (fd == -1) |
1cff166d | 25 | die ("cannot create DHCP socket : %s", NULL); |
44a2a316 | 26 | |
fd9fa481 SK |
27 | if ((flags = fcntl(fd, F_GETFL, 0)) == -1 || |
28 | fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 || | |
44a2a316 | 29 | #if defined(IP_PKTINFO) |
3be34541 | 30 | setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 || |
44a2a316 | 31 | #elif defined(IP_RECVIF) |
3be34541 | 32 | setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 || |
44a2a316 | 33 | #endif |
3be34541 | 34 | setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1) |
44a2a316 SK |
35 | die("failed to set options on DHCP socket: %s", NULL); |
36 | ||
37 | saddr.sin_family = AF_INET; | |
38 | saddr.sin_port = htons(DHCP_SERVER_PORT); | |
39 | saddr.sin_addr.s_addr = INADDR_ANY; | |
3be34541 SK |
40 | #ifdef HAVE_SOCKADDR_SA_LEN |
41 | saddr.sin_len = sizeof(struct sockaddr_in); | |
42 | #endif | |
43 | ||
44a2a316 SK |
44 | if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in))) |
45 | die("failed to bind DHCP server socket: %s", NULL); | |
46 | ||
3be34541 SK |
47 | daemon->dhcpfd = fd; |
48 | ||
49 | if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 || | |
50 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) || | |
51 | setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1) | |
52 | die("cannot create ICMP raw socket: %s.", NULL); | |
53 | ||
54 | daemon->dhcp_icmp_fd = fd; | |
44a2a316 SK |
55 | |
56 | #ifdef HAVE_BPF | |
3be34541 SK |
57 | { |
58 | int i = 0; | |
59 | while (1) | |
60 | { | |
61 | char filename[50]; | |
62 | sprintf(filename, "/dev/bpf%d", i++); | |
63 | if ((fd = open(filename, O_RDWR, 0)) != -1) | |
64 | break; | |
65 | if (errno != EBUSY) | |
66 | die("cannot create DHCP BPF socket: %s", NULL); | |
67 | } | |
68 | } | |
44a2a316 | 69 | #else |
3be34541 SK |
70 | /* since we don't ever use the packet socket for reception, |
71 | and it receives copies of _all_ IP packets, then that data | |
72 | will build up in kernel buffers, wasting memory. Set the | |
73 | socket receive buffer size to one to avoid that. (zero is | |
74 | rejected as non-sensical by some BSD kernels) */ | |
75 | if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 || | |
fd9fa481 SK |
76 | (flags = fcntl(fd, F_GETFL, 0)) == -1 || |
77 | fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 || | |
3be34541 SK |
78 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1) |
79 | die("cannot create DHCP packet socket: %s. " | |
80 | "Is CONFIG_PACKET enabled in your kernel?", NULL); | |
44a2a316 SK |
81 | #endif |
82 | ||
3be34541 SK |
83 | daemon->dhcp_raw_fd = fd; |
84 | ||
dfa666f2 SK |
85 | /* If the same IP appears in more than one host config, then DISCOVER |
86 | for one of the hosts will get the address, but REQUEST will be NAKed, | |
87 | since the address is reserved by the other one -> protocol loop. */ | |
3be34541 | 88 | for (configs = daemon->dhcp_conf; configs; configs = configs->next) |
dfa666f2 SK |
89 | for (cp = configs->next; cp; cp = cp->next) |
90 | if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr) | |
3be34541 SK |
91 | die("duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr)); |
92 | ||
93 | daemon->dhcp_packet = safe_malloc(sizeof(struct udp_dhcp_packet)); | |
94 | /* These two each hold a DHCP option max size 256 | |
95 | and get a terminating zero added */ | |
96 | daemon->dhcp_buff = safe_malloc(257); | |
97 | daemon->dhcp_buff2 = safe_malloc(257); | |
98 | ||
44a2a316 SK |
99 | } |
100 | ||
3be34541 | 101 | void dhcp_packet(struct daemon *daemon, time_t now) |
9e4abcb5 | 102 | { |
3be34541 SK |
103 | struct udp_dhcp_packet *rawpacket = daemon->dhcp_packet; |
104 | struct dhcp_packet *mess = &rawpacket->data; | |
44a2a316 SK |
105 | struct dhcp_context *context; |
106 | struct iname *tmp; | |
107 | struct ifreq ifr; | |
108 | struct msghdr msg; | |
109 | struct iovec iov[2]; | |
110 | struct cmsghdr *cmptr; | |
111 | int sz, newlen, iface_index = 0; | |
3be34541 | 112 | struct in_addr iface_netmask, iface_addr, iface_broadcast; |
44a2a316 SK |
113 | #ifdef HAVE_BPF |
114 | unsigned char iface_hwaddr[ETHER_ADDR_LEN]; | |
9e4abcb5 SK |
115 | #endif |
116 | ||
44a2a316 SK |
117 | union { |
118 | struct cmsghdr align; /* this ensures alignment */ | |
119 | #ifdef IP_PKTINFO | |
120 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; | |
121 | #else | |
122 | char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; | |
9e4abcb5 | 123 | #endif |
44a2a316 SK |
124 | } control_u; |
125 | ||
3be34541 SK |
126 | iov[0].iov_base = (char *)mess; |
127 | iov[0].iov_len = sizeof(struct dhcp_packet); | |
9e4abcb5 | 128 | |
44a2a316 SK |
129 | msg.msg_control = control_u.control; |
130 | msg.msg_controllen = sizeof(control_u); | |
131 | msg.msg_flags = 0; | |
132 | msg.msg_name = NULL; | |
133 | msg.msg_namelen = 0; | |
134 | msg.msg_iov = iov; | |
135 | msg.msg_iovlen = 1; | |
136 | ||
3be34541 | 137 | sz = recvmsg(daemon->dhcpfd, &msg, 0); |
44a2a316 SK |
138 | |
139 | if (sz < (int)(sizeof(*mess) - sizeof(mess->options))) | |
140 | return; | |
141 | ||
142 | #if defined (IP_PKTINFO) | |
143 | if (msg.msg_controllen < sizeof(struct cmsghdr)) | |
144 | return; | |
145 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
146 | if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) | |
147 | iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex; | |
148 | ||
8a911ccc | 149 | if (!(ifr.ifr_ifindex = iface_index) || |
3be34541 | 150 | ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1) |
44a2a316 | 151 | return; |
9e4abcb5 | 152 | |
44a2a316 SK |
153 | #elif defined(IP_RECVIF) |
154 | if (msg.msg_controllen < sizeof(struct cmsghdr)) | |
155 | return; | |
156 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
157 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) | |
158 | iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index; | |
159 | ||
160 | if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name)) | |
161 | return; | |
162 | ||
163 | #else | |
3be34541 SK |
164 | { |
165 | struct iname *name; | |
fd9fa481 | 166 | for (name = daemon->if_names; name->isloop; name = name->next); |
3be34541 SK |
167 | strcpy(ifr.ifr_name, name->name); |
168 | } | |
9e4abcb5 SK |
169 | #endif |
170 | ||
171 | #ifdef HAVE_BPF | |
44a2a316 | 172 | ifr.ifr_addr.sa_family = AF_LINK; |
3be34541 | 173 | if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0) |
44a2a316 SK |
174 | return; |
175 | memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN); | |
176 | #endif | |
177 | ||
178 | ifr.ifr_addr.sa_family = AF_INET; | |
3be34541 | 179 | if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 ) |
44a2a316 SK |
180 | return; |
181 | iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; | |
182 | ||
183 | /* enforce available interface configuration */ | |
3be34541 | 184 | for (tmp = daemon->if_except; tmp; tmp = tmp->next) |
44a2a316 SK |
185 | if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) |
186 | return; | |
187 | ||
3be34541 | 188 | if (daemon->if_names || daemon->if_addrs) |
44a2a316 | 189 | { |
3be34541 | 190 | for (tmp = daemon->if_names; tmp; tmp = tmp->next) |
44a2a316 SK |
191 | if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) |
192 | break; | |
193 | if (!tmp) | |
3be34541 | 194 | for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) |
44a2a316 SK |
195 | if (tmp->addr.sa.sa_family == AF_INET && |
196 | tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr) | |
197 | break; | |
198 | if (!tmp) | |
199 | return; | |
200 | } | |
201 | ||
3be34541 SK |
202 | iface_netmask.s_addr = 0; |
203 | iface_broadcast.s_addr = 0; | |
44a2a316 | 204 | |
3be34541 | 205 | for (context = daemon->dhcp; context; context = context->next) |
44a2a316 | 206 | { |
3be34541 SK |
207 | /* Fill in missing netmask and broadcast address values for any approriate |
208 | dhcp-ranges which match this interface and don't have them. */ | |
209 | if (!context->netmask.s_addr) | |
210 | { | |
211 | if (!iface_netmask.s_addr && ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1) | |
212 | iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; | |
213 | ||
214 | if (iface_netmask.s_addr && | |
36717eee SK |
215 | (is_same_net(iface_addr, context->start, iface_netmask) || |
216 | is_same_net(iface_addr, context->end, iface_netmask))) | |
217 | { | |
218 | context->netmask = iface_netmask; | |
219 | if (!(is_same_net(iface_addr, context->start, iface_netmask) && | |
220 | is_same_net(iface_addr, context->end, iface_netmask))) | |
221 | { | |
222 | strcpy(daemon->dhcp_buff, inet_ntoa(context->start)); | |
223 | strcpy(daemon->dhcp_buff2, inet_ntoa(context->end)); | |
224 | syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s", | |
225 | daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(iface_netmask)); | |
226 | } | |
227 | } | |
3be34541 SK |
228 | } |
229 | ||
230 | /* Determine "default" default routes. These are to this server or the relay agent. | |
231 | Also broadcast addresses, if not specified */ | |
232 | if (context->netmask.s_addr) | |
233 | { | |
234 | if (is_same_net(iface_addr, context->start, context->netmask)) | |
235 | { | |
236 | if (!context->router.s_addr) | |
237 | context->router = iface_addr; | |
238 | if (!context->broadcast.s_addr) | |
239 | { | |
240 | if (!iface_broadcast.s_addr && ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1) | |
241 | iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; | |
36717eee SK |
242 | if (iface_broadcast.s_addr && |
243 | is_same_net(iface_broadcast, context->start, context->netmask)) | |
3be34541 SK |
244 | context->broadcast = iface_broadcast; |
245 | else | |
246 | context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; | |
247 | } | |
248 | } | |
249 | else if (mess->giaddr.s_addr && is_same_net(mess->giaddr, context->start, context->netmask)) | |
250 | { | |
251 | if (!context->router.s_addr) | |
252 | context->router = mess->giaddr; | |
253 | /* fill in missing broadcast addresses for relayed ranges */ | |
254 | if (!context->broadcast.s_addr) | |
255 | context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; | |
256 | } | |
257 | } | |
44a2a316 SK |
258 | } |
259 | ||
44a2a316 | 260 | lease_prune(NULL, now); /* lose any expired leases */ |
3be34541 | 261 | newlen = dhcp_reply(daemon, iface_addr, ifr.ifr_name, sz, now); |
44a2a316 | 262 | lease_update_file(0, now); |
fd9fa481 | 263 | lease_update_dns(daemon); |
44a2a316 SK |
264 | |
265 | if (newlen == 0) | |
266 | return; | |
267 | ||
268 | if (mess->giaddr.s_addr || mess->ciaddr.s_addr) | |
269 | { | |
3be34541 | 270 | /* To send to BOOTP relay or configured client, use the IP packet */ |
44a2a316 SK |
271 | |
272 | struct sockaddr_in dest; | |
273 | dest.sin_family = AF_INET; | |
3be34541 SK |
274 | #ifdef HAVE_SOCKADDR_SA_LEN |
275 | dest.sin_len = sizeof(struct sockaddr_in); | |
276 | #endif | |
277 | ||
44a2a316 SK |
278 | if (mess->giaddr.s_addr) |
279 | { | |
280 | dest.sin_port = htons(DHCP_SERVER_PORT); | |
281 | dest.sin_addr = mess->giaddr; | |
9e4abcb5 | 282 | } |
44a2a316 SK |
283 | else |
284 | { | |
285 | dest.sin_port = htons(DHCP_CLIENT_PORT); | |
286 | dest.sin_addr = mess->ciaddr; | |
287 | } | |
288 | ||
fd9fa481 SK |
289 | while(sendto(daemon->dhcpfd, mess, newlen, 0, |
290 | (struct sockaddr *)&dest, sizeof(dest)) == -1 && | |
291 | retry_send()); | |
44a2a316 SK |
292 | } |
293 | else | |
294 | { | |
295 | /* Hairy stuff, packet either has to go to the | |
296 | net broadcast or the destination can't reply to ARP yet, | |
297 | but we do know the physical address. | |
298 | Build the packet by steam, and send directly, bypassing | |
299 | the kernel IP stack */ | |
300 | ||
301 | u32 i, sum; | |
302 | unsigned char hwdest[ETHER_ADDR_LEN]; | |
303 | ||
304 | if (ntohs(mess->flags) & 0x8000) | |
305 | { | |
306 | memset(hwdest, 255, ETHER_ADDR_LEN); | |
307 | rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST; | |
308 | } | |
309 | else | |
310 | { | |
311 | memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN); | |
312 | rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr; | |
313 | } | |
314 | ||
315 | rawpacket->ip.ip_p = IPPROTO_UDP; | |
316 | rawpacket->ip.ip_src.s_addr = iface_addr.s_addr; | |
317 | rawpacket->ip.ip_len = htons(sizeof(struct ip) + | |
318 | sizeof(struct udphdr) + | |
319 | newlen) ; | |
320 | rawpacket->ip.ip_hl = sizeof(struct ip) / 4; | |
321 | rawpacket->ip.ip_v = IPVERSION; | |
322 | rawpacket->ip.ip_tos = 0; | |
323 | rawpacket->ip.ip_id = htons(0); | |
324 | rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */ | |
325 | rawpacket->ip.ip_ttl = IPDEFTTL; | |
326 | rawpacket->ip.ip_sum = 0; | |
327 | for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++) | |
328 | sum += ((u16 *)&rawpacket->ip)[i]; | |
329 | while (sum>>16) | |
330 | sum = (sum & 0xffff) + (sum >> 16); | |
331 | rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum; | |
332 | ||
333 | rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT); | |
334 | rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT); | |
335 | ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */ | |
336 | rawpacket->udp.uh_sum = 0; | |
337 | rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen); | |
338 | sum += htons(IPPROTO_UDP); | |
339 | for (i = 0; i < 4; i++) | |
340 | sum += ((u16 *)&rawpacket->ip.ip_src)[i]; | |
341 | for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++) | |
342 | sum += ((u16 *)&rawpacket->udp)[i]; | |
343 | while (sum>>16) | |
344 | sum = (sum & 0xffff) + (sum >> 16); | |
345 | rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum; | |
346 | ||
347 | { | |
348 | #ifdef HAVE_BPF | |
349 | struct ether_header header; | |
350 | ||
351 | header.ether_type = htons(ETHERTYPE_IP); | |
352 | memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN); | |
353 | memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN); | |
354 | ||
3be34541 | 355 | ioctl(daemon->dhcp_raw_fd, BIOCSETIF, &ifr); |
44a2a316 SK |
356 | |
357 | iov[0].iov_base = (char *)&header; | |
358 | iov[0].iov_len = sizeof(struct ether_header); | |
359 | iov[1].iov_base = (char *)rawpacket; | |
360 | iov[1].iov_len = ntohs(rawpacket->ip.ip_len); | |
fd9fa481 SK |
361 | while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 && |
362 | errno == EINTR); | |
44a2a316 SK |
363 | #else |
364 | struct sockaddr_ll dest; | |
365 | ||
366 | dest.sll_family = AF_PACKET; | |
367 | dest.sll_halen = ETHER_ADDR_LEN; | |
368 | dest.sll_ifindex = iface_index; | |
369 | dest.sll_protocol = htons(ETHERTYPE_IP); | |
370 | memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN); | |
fd9fa481 SK |
371 | while (sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len), |
372 | 0, (struct sockaddr *)&dest, sizeof(dest)) == -1 && | |
373 | errno == EINTR); | |
44a2a316 SK |
374 | #endif |
375 | } | |
9e4abcb5 SK |
376 | } |
377 | } | |
378 | ||
379 | int address_available(struct dhcp_context *context, struct in_addr taddr) | |
380 | { | |
36717eee SK |
381 | /* Check is an address is OK for this network, check all |
382 | possible ranges. */ | |
9e4abcb5 | 383 | |
36717eee | 384 | unsigned int start, end, addr = ntohl(taddr.s_addr); |
9e4abcb5 | 385 | |
36717eee SK |
386 | for (; context; context = context->current) |
387 | { | |
388 | start = ntohl(context->start.s_addr); | |
389 | end = ntohl(context->end.s_addr); | |
9e4abcb5 | 390 | |
36717eee SK |
391 | if (!context->static_only && |
392 | addr >= start && | |
393 | addr <= end) | |
394 | return 1; | |
395 | } | |
9e4abcb5 | 396 | |
36717eee | 397 | return 0; |
9e4abcb5 | 398 | } |
dfa666f2 SK |
399 | |
400 | struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr) | |
401 | { | |
402 | struct dhcp_config *config; | |
403 | ||
404 | for (config = configs; config; config = config->next) | |
405 | if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) | |
406 | return config; | |
407 | ||
408 | return NULL; | |
409 | } | |
9e4abcb5 | 410 | |
3be34541 | 411 | int address_allocate(struct dhcp_context *context, struct daemon *daemon, |
a84fa1d0 | 412 | struct in_addr *addrp, unsigned char *hwaddr) |
9e4abcb5 | 413 | { |
feba5c1d | 414 | /* Find a free address: exclude anything in use and anything allocated to |
9e4abcb5 SK |
415 | a particular hwaddr/clientid/hostname in our configuration */ |
416 | ||
a84fa1d0 | 417 | struct in_addr start, addr ; |
feba5c1d | 418 | unsigned int i, j; |
a84fa1d0 | 419 | |
36717eee SK |
420 | for (; context; context = context->current) |
421 | if (!context->static_only) | |
9e4abcb5 | 422 | { |
36717eee SK |
423 | /* pick a seed based on hwaddr then iterate until we find a free address. */ |
424 | for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++) | |
425 | j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16); | |
426 | ||
427 | start.s_addr = addr.s_addr = | |
428 | htonl(ntohl(context->start.s_addr) + | |
429 | (j % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr)))); | |
430 | ||
431 | do { | |
432 | if (!lease_find_by_addr(addr) && | |
433 | !config_find_by_address(daemon->dhcp_conf, addr)) | |
434 | { | |
435 | if (icmp_ping(daemon, addr)) | |
436 | /* perturb address selection so that we are | |
437 | less likely to try this address again. */ | |
438 | context->addr_epoch++; | |
439 | else | |
440 | { | |
441 | *addrp = addr; | |
442 | return 1; | |
443 | } | |
444 | } | |
3be34541 | 445 | |
36717eee SK |
446 | addr.s_addr = htonl(ntohl(addr.s_addr) + 1); |
447 | ||
448 | if (addr.s_addr == htonl(ntohl(context->end.s_addr) + 1)) | |
449 | addr = context->start; | |
450 | ||
451 | } while (addr.s_addr != start.s_addr); | |
452 | } | |
9e4abcb5 SK |
453 | return 0; |
454 | } | |
455 | ||
456 | static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config) | |
457 | { | |
458 | if (!context) | |
459 | return 1; | |
33820b7e | 460 | if (!(config->flags & CONFIG_ADDR)) |
9e4abcb5 | 461 | return 1; |
a84fa1d0 | 462 | if (is_same_net(config->addr, context->start, context->netmask)) |
9e4abcb5 SK |
463 | return 1; |
464 | ||
465 | return 0; | |
466 | } | |
467 | ||
468 | struct dhcp_config *find_config(struct dhcp_config *configs, | |
469 | struct dhcp_context *context, | |
470 | unsigned char *clid, int clid_len, | |
471 | unsigned char *hwaddr, char *hostname) | |
472 | { | |
473 | struct dhcp_config *config; | |
474 | ||
475 | if (clid_len) | |
476 | for (config = configs; config; config = config->next) | |
33820b7e SK |
477 | if (config->flags & CONFIG_CLID) |
478 | { | |
479 | if (config->clid_len == clid_len && | |
480 | memcmp(config->clid, clid, clid_len) == 0 && | |
481 | is_addr_in_context(context, config)) | |
482 | return config; | |
483 | ||
484 | /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and | |
485 | cope with that here */ | |
486 | if (*clid == 0 && config->clid_len == clid_len-1 && | |
487 | memcmp(config->clid, clid+1, clid_len-1) == 0 && | |
488 | is_addr_in_context(context, config)) | |
489 | return config; | |
490 | } | |
491 | ||
9e4abcb5 | 492 | for (config = configs; config; config = config->next) |
33820b7e SK |
493 | if ((config->flags & CONFIG_HWADDR) && |
494 | memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 && | |
9e4abcb5 SK |
495 | is_addr_in_context(context, config)) |
496 | return config; | |
497 | ||
498 | if (hostname) | |
499 | for (config = configs; config; config = config->next) | |
33820b7e SK |
500 | if ((config->flags & CONFIG_NAME) && |
501 | hostname_isequal(config->hostname, hostname) && | |
9e4abcb5 SK |
502 | is_addr_in_context(context, config)) |
503 | return config; | |
504 | ||
505 | return NULL; | |
506 | } | |
507 | ||
3be34541 | 508 | void dhcp_read_ethers(struct daemon *daemon) |
44a2a316 SK |
509 | { |
510 | FILE *f = fopen(ETHERSFILE, "r"); | |
33820b7e | 511 | unsigned int flags, e0, e1, e2, e3, e4, e5; |
3be34541 | 512 | char *buff = daemon->namebuff; |
33820b7e | 513 | char *ip, *cp; |
44a2a316 | 514 | struct in_addr addr; |
33820b7e | 515 | unsigned char hwaddr[ETHER_ADDR_LEN]; |
3be34541 | 516 | struct dhcp_config *config, *configs = daemon->dhcp_conf; |
33820b7e | 517 | int count = 0; |
44a2a316 SK |
518 | |
519 | if (!f) | |
33820b7e SK |
520 | { |
521 | syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m"); | |
3be34541 | 522 | return; |
33820b7e SK |
523 | } |
524 | ||
44a2a316 SK |
525 | while (fgets(buff, MAXDNAME, f)) |
526 | { | |
33820b7e | 527 | while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1])) |
44a2a316 SK |
528 | buff[strlen(buff)-1] = 0; |
529 | ||
530 | if ((*buff == '#') || (*buff == '+')) | |
531 | continue; | |
532 | ||
33820b7e SK |
533 | for (ip = buff; *ip && !isspace(*ip); ip++); |
534 | for(; *ip && isspace(*ip); ip++) | |
44a2a316 SK |
535 | *ip = 0; |
536 | if (!*ip) | |
537 | continue; | |
538 | ||
539 | if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5)) | |
540 | continue; | |
541 | ||
33820b7e SK |
542 | hwaddr[0] = e0; |
543 | hwaddr[1] = e1; | |
544 | hwaddr[2] = e2; | |
545 | hwaddr[3] = e3; | |
546 | hwaddr[4] = e4; | |
547 | hwaddr[5] = e5; | |
548 | ||
44a2a316 SK |
549 | /* check for name or dotted-quad */ |
550 | for (cp = ip; *cp; cp++) | |
551 | if (!(*cp == '.' || (*cp >='0' && *cp <= '9'))) | |
552 | break; | |
553 | ||
554 | if (!*cp) | |
555 | { | |
44a2a316 | 556 | if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1) |
1cff166d | 557 | continue; |
33820b7e | 558 | flags = CONFIG_ADDR; |
1cff166d SK |
559 | |
560 | for (config = configs; config; config = config->next) | |
33820b7e | 561 | if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) |
1cff166d | 562 | break; |
44a2a316 SK |
563 | } |
564 | else | |
565 | { | |
1cff166d SK |
566 | if (!canonicalise(ip)) |
567 | continue; | |
33820b7e | 568 | flags = CONFIG_NAME; |
1cff166d SK |
569 | |
570 | for (config = configs; config; config = config->next) | |
33820b7e | 571 | if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip)) |
1cff166d | 572 | break; |
44a2a316 SK |
573 | } |
574 | ||
1cff166d SK |
575 | if (!config) |
576 | { | |
33820b7e SK |
577 | for (config = configs; config; config = config->next) |
578 | if ((config->flags & CONFIG_HWADDR) && | |
579 | memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0) | |
580 | break; | |
581 | ||
582 | if (!config) | |
583 | { | |
584 | if (!(config = malloc(sizeof(struct dhcp_config)))) | |
585 | continue; | |
586 | config->flags = 0; | |
587 | config->next = configs; | |
588 | configs = config; | |
589 | } | |
590 | ||
591 | config->flags |= flags; | |
592 | ||
593 | if (flags & CONFIG_NAME) | |
594 | { | |
595 | if ((config->hostname = malloc(strlen(ip)+1))) | |
596 | strcpy(config->hostname, ip); | |
597 | else | |
598 | config->flags &= ~CONFIG_NAME; | |
599 | } | |
600 | ||
601 | if (flags & CONFIG_ADDR) | |
602 | config->addr = addr; | |
1cff166d | 603 | } |
33820b7e | 604 | |
de37951c | 605 | config->flags |= CONFIG_HWADDR | CONFIG_NOCLID; |
33820b7e | 606 | memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN); |
1cff166d | 607 | |
33820b7e | 608 | count++; |
44a2a316 SK |
609 | } |
610 | ||
611 | fclose(f); | |
33820b7e SK |
612 | |
613 | syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count); | |
3be34541 SK |
614 | |
615 | daemon->dhcp_conf = configs; | |
44a2a316 SK |
616 | } |
617 | ||
618 | void dhcp_update_configs(struct dhcp_config *configs) | |
1ab84e2f | 619 | { |
44a2a316 SK |
620 | /* Some people like to keep all static IP addresses in /etc/hosts. |
621 | This goes through /etc/hosts and sets static addresses for any DHCP config | |
622 | records which don't have an address and whose name matches. */ | |
623 | ||
1ab84e2f SK |
624 | struct dhcp_config *config; |
625 | struct crec *crec; | |
44a2a316 | 626 | |
1ab84e2f | 627 | for (config = configs; config; config = config->next) |
33820b7e SK |
628 | if (!(config->flags & CONFIG_ADDR) && |
629 | (config->flags & CONFIG_NAME) && | |
1ab84e2f SK |
630 | (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) && |
631 | (crec->flags & F_HOSTS)) | |
33820b7e | 632 | { |
fd9fa481 | 633 | config->addr = crec->addr.addr.addr.addr4; |
33820b7e SK |
634 | config->flags |= CONFIG_ADDR; |
635 | } | |
1ab84e2f | 636 | } |
44a2a316 | 637 |