blob: 05b7e267b9d9ccafe8b9a8a8b006e370909bff46 [file] [log] [blame]
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001/* 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
Simon Kelley3be34542004-09-11 19:12:13 +010017void dhcp_init(struct daemon *daemon)
Simon Kelley44a2a312004-03-10 20:04:35 +000018{
19 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
20 struct sockaddr_in saddr;
Simon Kelley3be34542004-09-11 19:12:13 +010021 int oneopt = 1, zeroopt = 0;
22 struct dhcp_config *configs, *cp;
Simon Kelleydfa666f2004-08-02 18:27:27 +010023
Simon Kelley44a2a312004-03-10 20:04:35 +000024 if (fd == -1)
Simon Kelley1cff1662004-03-12 08:12:58 +000025 die ("cannot create DHCP socket : %s", NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000026
Simon Kelley3be34542004-09-11 19:12:13 +010027 if (
Simon Kelley44a2a312004-03-10 20:04:35 +000028#if defined(IP_PKTINFO)
Simon Kelley3be34542004-09-11 19:12:13 +010029 setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000030#elif defined(IP_RECVIF)
Simon Kelley3be34542004-09-11 19:12:13 +010031 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000032#endif
Simon Kelley3be34542004-09-11 19:12:13 +010033 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +000034 die("failed to set options on DHCP socket: %s", NULL);
35
36 saddr.sin_family = AF_INET;
37 saddr.sin_port = htons(DHCP_SERVER_PORT);
38 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010039#ifdef HAVE_SOCKADDR_SA_LEN
40 saddr.sin_len = sizeof(struct sockaddr_in);
41#endif
42
Simon Kelley44a2a312004-03-10 20:04:35 +000043 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
44 die("failed to bind DHCP server socket: %s", NULL);
45
Simon Kelley3be34542004-09-11 19:12:13 +010046 daemon->dhcpfd = fd;
47
48 if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
49 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
50 setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
51 die("cannot create ICMP raw socket: %s.", NULL);
52
53 daemon->dhcp_icmp_fd = fd;
Simon Kelley44a2a312004-03-10 20:04:35 +000054
55#ifdef HAVE_BPF
Simon Kelley3be34542004-09-11 19:12:13 +010056 {
57 int i = 0;
58 while (1)
59 {
60 char filename[50];
61 sprintf(filename, "/dev/bpf%d", i++);
62 if ((fd = open(filename, O_RDWR, 0)) != -1)
63 break;
64 if (errno != EBUSY)
65 die("cannot create DHCP BPF socket: %s", NULL);
66 }
67 }
Simon Kelley44a2a312004-03-10 20:04:35 +000068#else
Simon Kelley3be34542004-09-11 19:12:13 +010069 /* since we don't ever use the packet socket for reception,
70 and it receives copies of _all_ IP packets, then that data
71 will build up in kernel buffers, wasting memory. Set the
72 socket receive buffer size to one to avoid that. (zero is
73 rejected as non-sensical by some BSD kernels) */
74 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 ||
75 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1)
76 die("cannot create DHCP packet socket: %s. "
77 "Is CONFIG_PACKET enabled in your kernel?", NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000078#endif
79
Simon Kelley3be34542004-09-11 19:12:13 +010080 daemon->dhcp_raw_fd = fd;
81
Simon Kelleydfa666f2004-08-02 18:27:27 +010082 /* If the same IP appears in more than one host config, then DISCOVER
83 for one of the hosts will get the address, but REQUEST will be NAKed,
84 since the address is reserved by the other one -> protocol loop. */
Simon Kelley3be34542004-09-11 19:12:13 +010085 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
Simon Kelleydfa666f2004-08-02 18:27:27 +010086 for (cp = configs->next; cp; cp = cp->next)
87 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
Simon Kelley3be34542004-09-11 19:12:13 +010088 die("duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr));
89
90 daemon->dhcp_packet = safe_malloc(sizeof(struct udp_dhcp_packet));
91 /* These two each hold a DHCP option max size 256
92 and get a terminating zero added */
93 daemon->dhcp_buff = safe_malloc(257);
94 daemon->dhcp_buff2 = safe_malloc(257);
95
Simon Kelley44a2a312004-03-10 20:04:35 +000096}
97
Simon Kelley3be34542004-09-11 19:12:13 +010098void dhcp_packet(struct daemon *daemon, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000099{
Simon Kelley3be34542004-09-11 19:12:13 +0100100 struct udp_dhcp_packet *rawpacket = daemon->dhcp_packet;
101 struct dhcp_packet *mess = &rawpacket->data;
Simon Kelley44a2a312004-03-10 20:04:35 +0000102 struct dhcp_context *context;
103 struct iname *tmp;
104 struct ifreq ifr;
105 struct msghdr msg;
106 struct iovec iov[2];
107 struct cmsghdr *cmptr;
108 int sz, newlen, iface_index = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100109 struct in_addr iface_netmask, iface_addr, iface_broadcast;
Simon Kelley44a2a312004-03-10 20:04:35 +0000110#ifdef HAVE_BPF
111 unsigned char iface_hwaddr[ETHER_ADDR_LEN];
112#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000113
Simon Kelley44a2a312004-03-10 20:04:35 +0000114 union {
115 struct cmsghdr align; /* this ensures alignment */
116#ifdef IP_PKTINFO
117 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
118#else
119 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
120#endif
121 } control_u;
122
Simon Kelley3be34542004-09-11 19:12:13 +0100123 iov[0].iov_base = (char *)mess;
124 iov[0].iov_len = sizeof(struct dhcp_packet);
Simon Kelley44a2a312004-03-10 20:04:35 +0000125
126 msg.msg_control = control_u.control;
127 msg.msg_controllen = sizeof(control_u);
128 msg.msg_flags = 0;
129 msg.msg_name = NULL;
130 msg.msg_namelen = 0;
131 msg.msg_iov = iov;
132 msg.msg_iovlen = 1;
133
Simon Kelley3be34542004-09-11 19:12:13 +0100134 sz = recvmsg(daemon->dhcpfd, &msg, 0);
Simon Kelley44a2a312004-03-10 20:04:35 +0000135
136 if (sz < (int)(sizeof(*mess) - sizeof(mess->options)))
137 return;
138
139#if defined (IP_PKTINFO)
140 if (msg.msg_controllen < sizeof(struct cmsghdr))
141 return;
142 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
143 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
144 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
145
Simon Kelley8a911cc2004-03-16 18:35:52 +0000146 if (!(ifr.ifr_ifindex = iface_index) ||
Simon Kelley3be34542004-09-11 19:12:13 +0100147 ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000148 return;
149
150#elif defined(IP_RECVIF)
151 if (msg.msg_controllen < sizeof(struct cmsghdr))
152 return;
153 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
154 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
155 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
156
157 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
158 return;
159
160#else
Simon Kelley3be34542004-09-11 19:12:13 +0100161 {
162 struct iname *name;
163 for (name = daemon->if_names; names->isloop; names = names->next);
164 strcpy(ifr.ifr_name, name->name);
165 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000166#endif
167
168#ifdef HAVE_BPF
Simon Kelley44a2a312004-03-10 20:04:35 +0000169 ifr.ifr_addr.sa_family = AF_LINK;
Simon Kelley3be34542004-09-11 19:12:13 +0100170 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000171 return;
172 memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
173#endif
174
175 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100176 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
Simon Kelley44a2a312004-03-10 20:04:35 +0000177 return;
178 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
179
180 /* enforce available interface configuration */
Simon Kelley3be34542004-09-11 19:12:13 +0100181 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000182 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
183 return;
184
Simon Kelley3be34542004-09-11 19:12:13 +0100185 if (daemon->if_names || daemon->if_addrs)
Simon Kelley44a2a312004-03-10 20:04:35 +0000186 {
Simon Kelley3be34542004-09-11 19:12:13 +0100187 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000188 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
189 break;
190 if (!tmp)
Simon Kelley3be34542004-09-11 19:12:13 +0100191 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000192 if (tmp->addr.sa.sa_family == AF_INET &&
193 tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
194 break;
195 if (!tmp)
196 return;
197 }
198
Simon Kelley3be34542004-09-11 19:12:13 +0100199 iface_netmask.s_addr = 0;
200 iface_broadcast.s_addr = 0;
201
202 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000203 {
Simon Kelley3be34542004-09-11 19:12:13 +0100204 /* Fill in missing netmask and broadcast address values for any approriate
205 dhcp-ranges which match this interface and don't have them. */
206 if (!context->netmask.s_addr)
207 {
208 if (!iface_netmask.s_addr && ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
209 iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
210
211 if (iface_netmask.s_addr &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100212 (is_same_net(iface_addr, context->start, iface_netmask) ||
213 is_same_net(iface_addr, context->end, iface_netmask)))
214 {
215 context->netmask = iface_netmask;
216 if (!(is_same_net(iface_addr, context->start, iface_netmask) &&
217 is_same_net(iface_addr, context->end, iface_netmask)))
218 {
219 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
220 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
221 syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s",
222 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(iface_netmask));
223 }
224 }
Simon Kelley3be34542004-09-11 19:12:13 +0100225 }
226
227 /* Determine "default" default routes. These are to this server or the relay agent.
228 Also broadcast addresses, if not specified */
229 if (context->netmask.s_addr)
230 {
231 if (is_same_net(iface_addr, context->start, context->netmask))
232 {
233 if (!context->router.s_addr)
234 context->router = iface_addr;
235 if (!context->broadcast.s_addr)
236 {
237 if (!iface_broadcast.s_addr && ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
238 iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
Simon Kelley36717ee2004-09-20 19:20:58 +0100239 if (iface_broadcast.s_addr &&
240 is_same_net(iface_broadcast, context->start, context->netmask))
Simon Kelley3be34542004-09-11 19:12:13 +0100241 context->broadcast = iface_broadcast;
242 else
243 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
244 }
245 }
246 else if (mess->giaddr.s_addr && is_same_net(mess->giaddr, context->start, context->netmask))
247 {
248 if (!context->router.s_addr)
249 context->router = mess->giaddr;
250 /* fill in missing broadcast addresses for relayed ranges */
251 if (!context->broadcast.s_addr)
252 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
253 }
254 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000255 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000256
257 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley3be34542004-09-11 19:12:13 +0100258 newlen = dhcp_reply(daemon, iface_addr, ifr.ifr_name, sz, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000259 lease_update_file(0, now);
260 lease_update_dns();
Simon Kelley44a2a312004-03-10 20:04:35 +0000261
262 if (newlen == 0)
263 return;
264
265 if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
266 {
Simon Kelley3be34542004-09-11 19:12:13 +0100267 /* To send to BOOTP relay or configured client, use the IP packet */
Simon Kelley44a2a312004-03-10 20:04:35 +0000268
269 struct sockaddr_in dest;
270 dest.sin_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100271#ifdef HAVE_SOCKADDR_SA_LEN
272 dest.sin_len = sizeof(struct sockaddr_in);
273#endif
274
Simon Kelley44a2a312004-03-10 20:04:35 +0000275 if (mess->giaddr.s_addr)
276 {
277 dest.sin_port = htons(DHCP_SERVER_PORT);
278 dest.sin_addr = mess->giaddr;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000279 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000280 else
281 {
282 dest.sin_port = htons(DHCP_CLIENT_PORT);
283 dest.sin_addr = mess->ciaddr;
284 }
285
Simon Kelley3be34542004-09-11 19:12:13 +0100286 sendto(daemon->dhcpfd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
Simon Kelley44a2a312004-03-10 20:04:35 +0000287 }
288 else
289 {
290 /* Hairy stuff, packet either has to go to the
291 net broadcast or the destination can't reply to ARP yet,
292 but we do know the physical address.
293 Build the packet by steam, and send directly, bypassing
294 the kernel IP stack */
295
296 u32 i, sum;
297 unsigned char hwdest[ETHER_ADDR_LEN];
298
299 if (ntohs(mess->flags) & 0x8000)
300 {
301 memset(hwdest, 255, ETHER_ADDR_LEN);
302 rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
303 }
304 else
305 {
306 memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN);
307 rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
308 }
309
310 rawpacket->ip.ip_p = IPPROTO_UDP;
311 rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
312 rawpacket->ip.ip_len = htons(sizeof(struct ip) +
313 sizeof(struct udphdr) +
314 newlen) ;
315 rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
316 rawpacket->ip.ip_v = IPVERSION;
317 rawpacket->ip.ip_tos = 0;
318 rawpacket->ip.ip_id = htons(0);
319 rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
320 rawpacket->ip.ip_ttl = IPDEFTTL;
321 rawpacket->ip.ip_sum = 0;
322 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
323 sum += ((u16 *)&rawpacket->ip)[i];
324 while (sum>>16)
325 sum = (sum & 0xffff) + (sum >> 16);
326 rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
327
328 rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
329 rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
330 ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
331 rawpacket->udp.uh_sum = 0;
332 rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
333 sum += htons(IPPROTO_UDP);
334 for (i = 0; i < 4; i++)
335 sum += ((u16 *)&rawpacket->ip.ip_src)[i];
336 for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
337 sum += ((u16 *)&rawpacket->udp)[i];
338 while (sum>>16)
339 sum = (sum & 0xffff) + (sum >> 16);
340 rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
341
342 {
343#ifdef HAVE_BPF
344 struct ether_header header;
345
346 header.ether_type = htons(ETHERTYPE_IP);
347 memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
348 memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
349
Simon Kelley3be34542004-09-11 19:12:13 +0100350 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, &ifr);
Simon Kelley44a2a312004-03-10 20:04:35 +0000351
352 iov[0].iov_base = (char *)&header;
353 iov[0].iov_len = sizeof(struct ether_header);
354 iov[1].iov_base = (char *)rawpacket;
355 iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
Simon Kelley3be34542004-09-11 19:12:13 +0100356 writev(daemon->dhcp_raw_fd, iov, 2);
Simon Kelley44a2a312004-03-10 20:04:35 +0000357#else
358 struct sockaddr_ll dest;
359
360 dest.sll_family = AF_PACKET;
361 dest.sll_halen = ETHER_ADDR_LEN;
362 dest.sll_ifindex = iface_index;
363 dest.sll_protocol = htons(ETHERTYPE_IP);
364 memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
Simon Kelley3be34542004-09-11 19:12:13 +0100365 sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
Simon Kelley44a2a312004-03-10 20:04:35 +0000366 0, (struct sockaddr *)&dest, sizeof(dest));
367
368#endif
369 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000370 }
371}
372
373int address_available(struct dhcp_context *context, struct in_addr taddr)
374{
Simon Kelley36717ee2004-09-20 19:20:58 +0100375 /* Check is an address is OK for this network, check all
376 possible ranges. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000377
Simon Kelley36717ee2004-09-20 19:20:58 +0100378 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000379
Simon Kelley36717ee2004-09-20 19:20:58 +0100380 for (; context; context = context->current)
381 {
382 start = ntohl(context->start.s_addr);
383 end = ntohl(context->end.s_addr);
Simon Kelley3be34542004-09-11 19:12:13 +0100384
Simon Kelley36717ee2004-09-20 19:20:58 +0100385 if (!context->static_only &&
386 addr >= start &&
387 addr <= end)
388 return 1;
389 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000390
Simon Kelley36717ee2004-09-20 19:20:58 +0100391 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000392}
Simon Kelleydfa666f2004-08-02 18:27:27 +0100393
394struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
395{
396 struct dhcp_config *config;
397
398 for (config = configs; config; config = config->next)
399 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
400 return config;
401
402 return NULL;
403}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000404
Simon Kelley3be34542004-09-11 19:12:13 +0100405int address_allocate(struct dhcp_context *context, struct daemon *daemon,
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100406 struct in_addr *addrp, unsigned char *hwaddr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000407{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100408 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000409 a particular hwaddr/clientid/hostname in our configuration */
410
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100411 struct in_addr start, addr ;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100412 unsigned int i, j;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100413
Simon Kelley36717ee2004-09-20 19:20:58 +0100414 for (; context; context = context->current)
415 if (!context->static_only)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000416 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100417 /* pick a seed based on hwaddr then iterate until we find a free address. */
418 for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
419 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
420
421 start.s_addr = addr.s_addr =
422 htonl(ntohl(context->start.s_addr) +
423 (j % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
424
425 do {
426 if (!lease_find_by_addr(addr) &&
427 !config_find_by_address(daemon->dhcp_conf, addr))
428 {
429 if (icmp_ping(daemon, addr))
430 /* perturb address selection so that we are
431 less likely to try this address again. */
432 context->addr_epoch++;
433 else
434 {
435 *addrp = addr;
436 return 1;
437 }
438 }
Simon Kelley3be34542004-09-11 19:12:13 +0100439
Simon Kelley36717ee2004-09-20 19:20:58 +0100440 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
441
442 if (addr.s_addr == htonl(ntohl(context->end.s_addr) + 1))
443 addr = context->start;
444
445 } while (addr.s_addr != start.s_addr);
446 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000447 return 0;
448}
449
450static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
451{
452 if (!context)
453 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100454 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000455 return 1;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100456 if (is_same_net(config->addr, context->start, context->netmask))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000457 return 1;
458
459 return 0;
460}
461
462struct dhcp_config *find_config(struct dhcp_config *configs,
463 struct dhcp_context *context,
464 unsigned char *clid, int clid_len,
465 unsigned char *hwaddr, char *hostname)
466{
467 struct dhcp_config *config;
468
469 if (clid_len)
470 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100471 if (config->flags & CONFIG_CLID)
472 {
473 if (config->clid_len == clid_len &&
474 memcmp(config->clid, clid, clid_len) == 0 &&
475 is_addr_in_context(context, config))
476 return config;
477
478 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
479 cope with that here */
480 if (*clid == 0 && config->clid_len == clid_len-1 &&
481 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
482 is_addr_in_context(context, config))
483 return config;
484 }
485
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000486 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100487 if ((config->flags & CONFIG_HWADDR) &&
488 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000489 is_addr_in_context(context, config))
490 return config;
491
492 if (hostname)
493 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100494 if ((config->flags & CONFIG_NAME) &&
495 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000496 is_addr_in_context(context, config))
497 return config;
498
499 return NULL;
500}
501
Simon Kelley3be34542004-09-11 19:12:13 +0100502void dhcp_read_ethers(struct daemon *daemon)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000503{
Simon Kelley44a2a312004-03-10 20:04:35 +0000504 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley33820b72004-04-03 21:10:00 +0100505 unsigned int flags, e0, e1, e2, e3, e4, e5;
Simon Kelley3be34542004-09-11 19:12:13 +0100506 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100507 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000508 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100509 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley3be34542004-09-11 19:12:13 +0100510 struct dhcp_config *config, *configs = daemon->dhcp_conf;
Simon Kelley33820b72004-04-03 21:10:00 +0100511 int count = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000512
513 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100514 {
515 syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m");
Simon Kelley3be34542004-09-11 19:12:13 +0100516 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100517 }
518
Simon Kelley44a2a312004-03-10 20:04:35 +0000519 while (fgets(buff, MAXDNAME, f))
520 {
Simon Kelley33820b72004-04-03 21:10:00 +0100521 while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000522 buff[strlen(buff)-1] = 0;
523
524 if ((*buff == '#') || (*buff == '+'))
525 continue;
526
Simon Kelley33820b72004-04-03 21:10:00 +0100527 for (ip = buff; *ip && !isspace(*ip); ip++);
528 for(; *ip && isspace(*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000529 *ip = 0;
530 if (!*ip)
531 continue;
532
533 if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
534 continue;
535
Simon Kelley33820b72004-04-03 21:10:00 +0100536 hwaddr[0] = e0;
537 hwaddr[1] = e1;
538 hwaddr[2] = e2;
539 hwaddr[3] = e3;
540 hwaddr[4] = e4;
541 hwaddr[5] = e5;
542
Simon Kelley44a2a312004-03-10 20:04:35 +0000543 /* check for name or dotted-quad */
544 for (cp = ip; *cp; cp++)
545 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
546 break;
547
548 if (!*cp)
549 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000550 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelley1cff1662004-03-12 08:12:58 +0000551 continue;
Simon Kelley33820b72004-04-03 21:10:00 +0100552 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000553
554 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100555 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000556 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000557 }
558 else
559 {
Simon Kelley1cff1662004-03-12 08:12:58 +0000560 if (!canonicalise(ip))
561 continue;
Simon Kelley33820b72004-04-03 21:10:00 +0100562 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000563
564 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100565 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000566 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000567 }
568
Simon Kelley1cff1662004-03-12 08:12:58 +0000569 if (!config)
570 {
Simon Kelley33820b72004-04-03 21:10:00 +0100571 for (config = configs; config; config = config->next)
572 if ((config->flags & CONFIG_HWADDR) &&
573 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
574 break;
575
576 if (!config)
577 {
578 if (!(config = malloc(sizeof(struct dhcp_config))))
579 continue;
580 config->flags = 0;
581 config->next = configs;
582 configs = config;
583 }
584
585 config->flags |= flags;
586
587 if (flags & CONFIG_NAME)
588 {
589 if ((config->hostname = malloc(strlen(ip)+1)))
590 strcpy(config->hostname, ip);
591 else
592 config->flags &= ~CONFIG_NAME;
593 }
594
595 if (flags & CONFIG_ADDR)
596 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000597 }
Simon Kelley33820b72004-04-03 21:10:00 +0100598
Simon Kelleyde379512004-06-22 20:23:33 +0100599 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100600 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelley1cff1662004-03-12 08:12:58 +0000601
Simon Kelley33820b72004-04-03 21:10:00 +0100602 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000603 }
604
605 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100606
607 syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count);
Simon Kelley3be34542004-09-11 19:12:13 +0100608
609 daemon->dhcp_conf = configs;
Simon Kelley44a2a312004-03-10 20:04:35 +0000610}
611
612void dhcp_update_configs(struct dhcp_config *configs)
613{
614 /* Some people like to keep all static IP addresses in /etc/hosts.
615 This goes through /etc/hosts and sets static addresses for any DHCP config
616 records which don't have an address and whose name matches. */
617
Simon Kelley1ab84e22004-01-29 16:48:35 +0000618 struct dhcp_config *config;
619 struct crec *crec;
Simon Kelley44a2a312004-03-10 20:04:35 +0000620
Simon Kelley1ab84e22004-01-29 16:48:35 +0000621 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100622 if (!(config->flags & CONFIG_ADDR) &&
623 (config->flags & CONFIG_NAME) &&
Simon Kelley1ab84e22004-01-29 16:48:35 +0000624 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
625 (crec->flags & F_HOSTS))
Simon Kelley33820b72004-04-03 21:10:00 +0100626 {
627 config->addr = crec->addr.addr.addr4;
628 config->flags |= CONFIG_ADDR;
629 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000630}
Simon Kelley44a2a312004-03-10 20:04:35 +0000631